hibernate and their stupid error messages ;)
So I have a Page, which has a Lock. Because lock only exists within a Page it was modelled as a component. Fine.
Unfortunately if a page is locked, sometimes all child pages need to be locked, and this potentially means 1000s of pages needing to be locked. As the lock is done via the object model, we very quickly run into memory problems. So I decided to model Lock as a seperate object (ta Chris May for the idea) with a link to the Page. 1000s of Lock objects is fine.
Anyways, I did this via one-to-one, with cascade="all-delete-orphan" on the Page, and creating a lock works. Deleting a page also deletes the lock, but how the flip do you remove just the lock! Calling page.setLock(null) fails. Calling lock.setPage(null) fails!
So in the end I gave up and modelled it as Page has many Locks. Page internally has a Set of locks, and setLock(Lock) just removes all existing locks and adds the new lock, so the set will only ever contain a single Lock.
Unfortunately I started getting this helpful error:
org.hibernate.StaleStateException: Batch update returned unexpected row count from update: 0 actual row count: 0 expected: 1 at org.hibernate.jdbc.BatchingBatcher.checkRowCount(BatchingBatcher.java:92) at org.hibernate.jdbc.BatchingBatcher.checkRowCounts(BatchingBatcher.java:78) at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:57) at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:174) at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:226) at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:139) at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:274) at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27) at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:730) at uk.ac.warwick.sbr.dao.hibernate.AbstractManipulatePageTestCase.flushAndRefreshSession(AbstractManipulatePageTestCase.java:62) at uk.ac.warwick.sbr.dao.hibernate.LockPageTestCase.testCreateAndDeleteLock(LockPageTestCase.java:60) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at junit.framework.TestCase.runTest(TestCase.java:154) at junit.framework.TestCase.runBare(TestCase.java:127) at junit.framework.TestResult$1.protect(TestResult.java:106) at junit.framework.TestResult.runProtected(TestResult.java:124) at junit.framework.TestResult.run(TestResult.java:109) at junit.framework.TestCase.run(TestCase.java:118) at junit.framework.TestSuite.runTest(TestSuite.java:208) at junit.framework.TestSuite.run(TestSuite.java:203) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:478) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:344) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
It turns out that setLock(null) added a null object to the Set of locks which hibernate could not then persist. This (I presume) happens because hibernate first does an insert to create the page lock record, then does a corresponding update lock.page to create the reference to the parent. Kinda fishy really. I would either expect hibernate to blow up saying cannot persist null, or to simply skip that entry. I wouldn't expect it to do the insert but not the update!
Not adding nulls to the set works fine. Not that I should be using a stinking one-2-many to model a one-2-one in the first place!!!!
Hibernate, love it or hate it, or in this situation, both!