Skip to content

Instantly share code, notes, and snippets.

@senolatac
Last active March 9, 2024 11:40
Show Gist options
  • Save senolatac/89a983052368859a5447bb9dd412d4c6 to your computer and use it in GitHub Desktop.
Save senolatac/89a983052368859a5447bb9dd412d4c6 to your computer and use it in GitHub Desktop.
Hibernate Version Control

How Hibernate handle optimistic-lock?

Let's see it on an example query.

Hibernate uses the version property in the WHERE clause of the executing UPDATE statement:

UPDATE
    test
SET
    name = :newName,
    version = :existVersion + 1
WHERE
    id = :id AND version = :existVersion

So, if version = :existVersion doesn't match, then a StaleStateExcetion is thrown, which will be wrapped in a JPA OptimisticLockException when bootstrapping Hibernate using JPA.

Example

Let's consider we have a entity like that:

class Test {
   Long id;
   String name;
   int version;
}

For this Test class, on update operation. To check version, we can follow different strategies:

  1. we can manually check versions:
update(Test request) {
   Test exist = repository.findById(request.id);
   if (exist.version != request.version) throw Exception();
   ...
}
  • Pros it looks performant because of no-extra-db-call
  • Cons but extra business-check and extra if-check
  1. we can directly send version to db and optimistic-lock handles it.
@Transactional
update(Test request) {
   Test exist = repository.findById(request.id);
   exist.setName(request.name);
   exist.setVersion(request.version);
   repository.update(exist);
}
  • This doesn't work. "Trying to set the version of an entity to a specific value is a mistake because the default optimistic locking mechanism does not take into consideration the version from the entity Java object but from the loading-time snapshot."
  • if we don't use Transaction, it works but not practical.
  1. secure-key and manual-update Let's assume, we have a version field in UUID format so in this case we couldn't benefit from hibernate @Version because hibernate supports only int and timestamp for version-field.
update(Test request) {
   Test exist = repository.findById(request.id);
   if (exist.version != request.version) throw Exception();
   exist.setVersion(new UUID...);
   repository.customUpdate(exist, request.version);
   ...
}
  • Cons custom update-query for each entity. and manual version control.
  • Pros it is secure but do we really need this security? it looks like over-engineering.
  1. secure-key and version Let's assume, we have a secure-key field in UUID and hibernate version but request/response doesn't know version.
update(Test request) {
   Test exist = repository.findById(request.id);
   if (exist.secureKey != request.secureKey) throw Exception();
   exist.setSecureKey(new UUID...);
   repository.update(exist);
   ...
}
  • Cons but extra business-check and extra if-check
  • Pros it is secure but do we really need this security? it looks like over-engineering.

References

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment