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?