Search code examples
hibernateweb-applicationsoptimistic-lockingsession-per-request

Best practice Hibernate optimistic locking and web application


I have a web application made with Tapestry5 (java webframework) and Hibernate. Now I'm trying to add optimistic locking. So I added a version attribute and the optimistic locking works, so that was easy and fast.

But as my web application works with "session-per-request" pattern, I'm not sure what's the best way of making use of this optimistic locking.

What happens:

UserA opens page with form which is loaded with values from entityA (version 1).

UserB opens page with form which is loaded with values from entityA (version 1).

UserA changes some values and submits the form. -> New request retrieves entityA (version 1) and commits changes (entityA is now version 2)

UserB changes some values and submits form. -> New request retrieves entityA (version 2) and commits changes (entityA is now version 3)

What should happen

Changes of UserB should not be committed. But because of session-per-request pattern the time window where optimistic locking error of Hibernate can occure is reduced to the timespan of just from the new request after the submit to the commit, which is not the desired result.

Possible solutions

After some research I found the following:

  • Add a hidden field to the form with the entities version

I could use this value to set the version of the entity before the commit, but Hibernate documentation doesn't recommend setting this value. Also it only works if the entity is detached and reattached, because else the manually set version value is ignored by Hibernate.

Secondly I could use this version value to do a manual check if the version, when the form was rendered, is the same as the version of the entity loaded in the request on submit. Then throw a OptimisticLockingException myself if needed.

  • Put the detached entity into HttpSession, so I don't have to load it again on submit

Conclusion

These methods work, but don't seem very practical to me and easy to make errors on. So I'm wondering how other people implement this problem.


Solution

  • I ended up implementing the following:

    • Add a hidden field to the form with the entities version
    • Use of form dto objects
    • Attaching these dto objects to the httpsession (conversational state across multiple requests)
    • Manual version check between dto and actual object