When attempting to update the same entity with two threads, the entity is retrieved from the DB in both threads. First thread completes the update, but the second thread gets a StaleStateException Here are the steps which Hibernate performs to update the entity in the second thread, starting from the point when it attempts to persist:
1. A PreparedStatement is constructed.
2. PreparedStatement is executed using the NonBatchingBatch.
3. StaleStateException is thrown by the second thread.
4. PreparedStatement is closed.
5. Transaction is rolled back.
6. I attempt to retry the update...
7. Entity is re-fetched from the DB.
8. Entity fields are updated.
9. Same session attempts to persist the entity to the DB.
10. NonBatchingBatch is still present in the JdbcCoordinator
12. AbstractEntityPersister#update:3049 gets the same NonBatchingBatch
13. AbstractEntityPersister attempts to execute previously closed PreparedStatement
14. java.sql.SQLException: Invalid state, the PreparedStatement object is closed.
Unfortunately, maybe we should. The "problem" is the org.hibernate.jdbc.Expectation contract as currently defined. It explicitly lists SQLException and HibernateException as possible exceptions. Ideally I would have limited Expectation to throwing JDBCException or StaleStateException.
FWIW, BatchingBatch handles any RuntimeException (in addition to SQLException). That seems sketchy, but not sure why I did that there.
I haven't found the time to come up with a test case, but I should be able to in the next few days.
While writing a unit test for 5.3.6, I was unable to reproduce the bug. The reason is AbstractEntityPersister line 3270. In 5.1.0, it doesn't compare the jdbcBatchSizeToUse, and useBatch is true even if jdbcBatchSizeToUse == 1. This leads to lines 3291-3294 being executed. In 5.3.6, it only uses a batch when jdbcBatchSizeToUse > 1, but then BatchBuilder lines 69-71 never return a NonBatchingBatch.
Unless a new class is written in the future, which extends AbstractEntityPersister, and overrides the update method, I don't see BatchBuilder ever returning a NonBatchingBatch.