How to reset Hibernate sequence generators?
Perhaps one of the solution is to run each JUNIT test in new sessionfactory. so open and close session factory using @Before
and @After
Pros
- you get sequence generators from first
Cons
- It takes a few more seconds to all JUNIT testcases
Update
Based on comment another way is to reset sequence in every JUNIT test in @Before method
ALTER SEQUENCE Test.sequence RESTART WITH 1
I was facing the same Problem and didn't find an inbuild way to do it. We're using hibernate 4.2.7.
After debugging deeply into hibernate I ended up extending the sequence generator. We create entities using the standard sequence generator:
@SequenceGenerator(name = "SomeSeq", sequenceName = "DB_SEQ", allocationSize = 50)
Hibernate creates an org.hibernate.id.SequenceHiLoGenerator for each entity. The SequnceHiLoGenerator delegates to an OptimizerFactory.LegacyHiLoAlgorithmOptimizer instance.
In order to reset the sequence counters and force syncing with the database sequence you have to reset internal variables in the LegacyHiLoAlgorithmOptimizer. Unfortunately these variables are private and not modifyable. I tried to find a way using inheritance but I didn't find an elegant solution. Finally I
created a source copy of the SequenceHiLoGenerator and extended it with a simple reset functionality:
public class ResetableIdGenerator extends SequenceGenerator {
public static int cycle = 0; // global, indicating current test cycle
protected int startingCycle = 0; // instance, indicating the test cycle the LegacyHiLoAlgorithmOptimizer was used the last time
[...]
public synchronized Serializable generate(final SessionImplementor session, Object obj) {
// create a new HiLoOptimizer if there's a new test cycle
if (startingCycle < cycle) {
hiloOptimizer = new OptimizerFactory.LegacyHiLoAlgorithmOptimizer(getIdentifierType().getReturnedClass(),
maxLo);
startingCycle = cycle;
}
[....]
Modify the entities to use the custom generator:
@GenericGenerator(name = "SomeSeq", strategy = "yourpackage.ResetableIdGenerator", parameters = {
@Parameter(name = "sequence", value = "DB_SEQ"), @Parameter(name = "max_lo", value = "49") })
Reset the sequence generator inbetween your test (@before or @after):
// reset Hibernate Sequences
ResetableIdGenerator.cycle++;
I know this isn't a good solution - It's a hack. But it works and maybe it helps to find a better solution.
EDIT 20170504: My initial post contained a mistake: The parameters "sequenceName" and "allocationSize" are JPA parameters. The GenericGenerator is from hibernate. Instead of "sequenceName" you have to use "sequence", instead of "allocationSize" you have to use "max_lo" and set it to allocationSize-1. I updated the code example. Sorry!
Today I faced the same problem. Since I couldn't find another solution, I tried to take the solution from OleG. Unfortunately meanwhile org.hibernate.id.SequenceGenerator
is marked deprecated. Therefore, I used the org.hibernate.id.enhanced.SequenceStyleGenerator
. If anyone else needs it, here is my customized solution:
public class ResettableSequenceStyleGenerator extends SequenceStyleGenerator {
private static int cycle = 0;
private int instanceCycle = cycle;
private Type configure_type = null;
private Properties configure_params = null;
private ServiceRegistry configure_serviceRegistry = null;
private Database registerExportables_database = null;
@Override
public void configure(Type type, Properties params, ServiceRegistry serviceRegistry) throws MappingException {
configure_type = type;
configure_params = params;
configure_serviceRegistry = serviceRegistry;
super.configure(type, params, serviceRegistry);
}
@Override
public Serializable generate(SharedSessionContractImplementor session, Object object) throws HibernateException {
if (instanceCycle != cycle) {
super.configure(configure_type, configure_params, configure_serviceRegistry);
super.registerExportables(registerExportables_database);
instanceCycle = cycle;
}
return super.generate(session, object);
}
@Override
public void registerExportables(Database database) {
registerExportables_database = database;
super.registerExportables(database);
}
public static void resetAllInstances() {
cycle++;
}
}
Set the ResettableSequenceStyleGenerator
as strategy in the GenericGenerator
annotation as described in OleG's article:
@GenericGenerator(name = "SomeSeq", strategy = "yourpackage.ResettableSequenceStyleGenerator", parameters = ...)
In my IntegrationTest class I then reset the sequences before each test method:
@Before
public void resetSequences() {
ResettableSequenceStyleGenerator.resetAllInstances();
}