Environment:
I have that User
entity :
@Entity
public class User implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer userId;
@Version
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "VERSION", length = 19)
private Date version;
@Column(nullable = false, length = 20)
private String login;
// Getters and Setters
}
I have a search page which lists users, then I click on a user to edit it (giving its userId
in the URL).
In the edit form, I store on the server the fields of that entity and when I save my User I do this :
User user = entityManager.find(User.class, userId)
user.setLogin(form.getLogin());
user.setVersion(form.getVersion());
user.setUserId(form.getUserId());
entityManager.merge(user);
Question:
So if I correctly understood optimistic locking with Hibernate, if I open 2 tabs in my browser to edit the same user, then update the login on the first tab, and then the login on the second tab, I should have an OptimisticLockException
shouldn't I ?
Actually, this is not the case on my application... I verified, the form.getVersion()
return the same value in both case, even if that in the second update, the user.version
has been updated by the first edit.
Am I missing something ?
The EntityManager
is produced @RequestScoped
(so I'm on two different EntityManager
s when I try to merge...).
I've tried to do a entityManager.lock(user, LockModeType.OPTIMISTIC_FORCE_INCREMENT)
before entityManager.merge(...)
(as said here), but it didn't help.
I'm using Seam 3 with JBoss 7.0.2.Final (which uses Hibernate 4).
Found another way of triggering optimistic locking manually. We can compare the cached Hibernate version with the version in the entity. I'm using spring data JPA, and added the ff. to the repository save implementation:
EntityEntry entityEntry = entityManager
.unwrap(SessionImplementor.class)
.getPersistenceContext()
.getEntry(entity);
//checked if a cached entry is present
if (entityEntry != null) {
//Compare cached version with the one in the entity
if (!Objects.equals(entityEntry.getVersion(), classMetadata.getVersion(entity))) {
throw new ObjectOptimisticLockingFailureException();
}
}
Actually I've found a way to do that... but I think it's not really efficient (because there is 1 more SELECT request).
User user = entityManager.find(User.class, userId)
user.setLogin(form.getLogin());
user.setVersion(form.getVersion());
user.setUserId(form.getUserId());
entityManager.detach(user);
entityManager.merge(user);
With entityManager.detach(user)
, hibernate now uses my setted version
value instead of its own copied somewhere value...
I just did some more research on this.
It is not allowed to modify the version field according to the JPA spec:
JPA 2.0 Final Spec, Section 3.4.2:
An entity may access the state of its version field or property or export a method for use by the application to access the version, but must not modify the version value. With the exception noted in section 4.10, only the persistence provider is permitted to set or update the value of the version attribute in the object.
Note: Section 4.10 refers to batch updates which ignore the version attribute.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With