Why envers was blowing up
Posted by danielmeyer on January 27, 2009
We’re experiencing an issue when we try to use envers to version a persistent object that holds a set of other persistent objects, when we’re using Spring’s JTA transaction manager. (It doesn’t happen when we use hibernate local transactions using Spring’s HibernateTransactionManager.) The commit of a one-to-many relationship (a Product and a Product Set), blows up with a weird error.
I had made a little test project with a greeting and a greeting set, to try to duplicate the issue apart from all the domain code. I was not able to duplicate the issue — my greeting and greeting set committed fine, and the versioning records were created normally.
My test was set up like this:
/**
* This test commits an empty greeting set, then commits a new greeting to the set. * The envers versioning works.
*/
@Test
public void testSimpleCreate() {
final String greeting = "Hola";
assertNull(greetingDao.findByGreeting(greeting));
final String setName = "Saludos";
GreetingSetPO greetingSet = new GreetingSetPO();
greetingSet.setName(setName);
greetingSetDao.create(greetingSet);
GreetingPO greetingPO = new GreetingPO();
greetingPO.setGreeting(greeting);
greetingPO.setGreetingSet(greetingSetDao.findByName(setName));
Long greetingId = greetingDao.create(greetingPO);
GreetingPO greetingId2 = greetingDao.findByGreeting(greeting);
assertEquals(greetingId, greetingId2.getId());
}
Notice that I’m committing the (empty) greeting set, then pointing the greeting to it and committing the greeting — two create() calls.
Today I talked with the domain guy and found out that the domain code is doing it a little differently:
/**
* This test creates a transient greeting set and a transient greeting, points them to each other, and then commits the
* set-with-the-greeting-in-it. This blows up.
*/
@Test
public void testComplexCreate() {
final String greeting = "Quid est nomen tibi?";
final String setName = "Latin greetings";
assertNull(greetingSetDao.findByName(setName));
assertNull(greetingDao.findByGreeting(greeting));
GreetingSetPO greetingSetPO = new GreetingSetPO();
greetingSetPO.setName(setName);
GreetingPO greetingPO = new GreetingPO();
greetingPO.setGreeting(greeting);
// Point the GreetingPO to its GreetingSetPO and vice versa
greetingPO.setGreetingSet(greetingSetPO);
Set<GreetingPO> greetings = new HashSet<GreetingPO>();
greetings.add(greetingPO);
greetingSetPO.setMembers(greetings);
greetingSetDao.create(greetingSetPO);
assertNotNull(greetingSetDao.findByName(setName));
assertNotNull(greetingDao.findByGreeting(greeting));
}
Notice that this time, instead of committing the empty greeting set, we create the greeting set and greeting, point them to each other, then commit the set-with-the-greeting-in-it.
This blows up just like the domain code. Yeah!
So now my questions are:
- Should this single-create() version work?
- If not, why does it work when we’re using Hibernate local transactions?