I have a @scheduled method. When it runs, it scoops up some rows with a status of "A" from the database, sets their statuses to "B" so they won't get selected again, and sends the associated objects to their fate via JMS.
That's all working fine.
As part of a test, I added a sort of reflector service activator that returns an acknowledgment. That works fine.
And I made another JMS service activator that sees the acknowledgment and sets the associated rows' statuses to "C".
So the status should change from A if unsent, to B when sent, and to C when acknowledged.
Here's my difficulty - Sometimes messages will be acknowledged before the scheduled method completes. This causes a race condition. That is, the status of the row is "B" in the scheduled method and "C" in the service activator that records the acknowledgment. If the service activator finishes first, the C will be committed. When the main completes, the "B" will be committed over the top of the "C".
How can I get a commit into the scheduled service? If I can do this, I can select my rows, update their statuses, commit, then send the associated classes onward. The generated DAOs don't include a commit statement, I assume with good reason.
It might help if you could provide us with some code samples so that we understand your problem a bit more clearly. However, it sounds like you might benefit from a more explicit locking strategy to better prevent race conditions in your persistence tier. Can you elaborate a bit more on your locking and transactional details?
Paul Tepper Fisher, author Spring Persistence with Hibernate
Practically speaking, I have two independent threads (?) that are trying to update the same row. The first thread finds a bunch of database rows, marks them "sent" and sends them via JMS to the 2nd thread (ultimately.) The 2nd thread receives these messages and marks the associated rows as "acknowledged."
If the first thread could change the rows' status and commit before sending, that would do the trick.
Or if the transaction was "autocommit" that would work too. I normally avoid autocommit but it's looking pretty good right about now.
Perhaps this is really a Hibernate question? Looking at your credentials I think I'm covered, however!
It's difficult to tell from your question how you have set-up your transaction strategy. Are you using standard spring transactions? If so, do you have a service facade with the transactional rules specified? If the conflict is happening between two different threads, I would recommend having each thread execute its changes within the context of a transaction, and you should then ensure that you are using a very conservative isolation-level.
Within Hibernate, are you using a locking strategy? One simple approach is to use optimistic locking, and ensure that your domain model includes a version field (you can add an integer field and specify @Version to denote a version field). This will allow Hibernate to verify an update by first checking the value of the row in question's version field against the in-memory version before attempting an update. If the version returned by the select doesn't match the version of the in-memory domain object, Hibernate can assume that there is a race condition. You can handle these conditions by catching org.springframework.dao.OptimisticLockingFailureException within a try block, when attempting to modify the contentious domain object.
While optimistic locking is a reasonably effective and performant strategy for concurrent modifications, it isn't ideal for more dynamic scenarios. You can instead employ a pessimistic locking strategy, although this will incur a performance penalty by locking the database table. You should perform locks within the context of a transaction, and either attempt to first load your domain object via a session.get(class, id, LockMode.UPGRADE), or call session.lock(), specifying a LockMode of UPGRADE or READ. Calling lock also forces a version check, so this can help prevent potential race conditions as well.
I hope this helps. Please forward along your domain model and transaction setup if you have further questions.