Java - JPA - @Version annotation
Every time an entity is updated in the database the version field will be increased by one. Every operation that updates the entity in the database will have appended WHERE version = VERSION_THAT_WAS_LOADED_FROM_DATABASE
to its query.
In checking affected rows of your operation the jpa framework can make sure there was no concurrent modification between loading and persisting your entity because the query would not find your entity in the database when it's version number has been increased between load and persist.
But still I am not sure how it works?
Let's say an entity MyEntity
has an annotated version
property:
@Entity
public class MyEntity implements Serializable {
@Id
@GeneratedValue
private Long id;
private String name;
@Version
private Long version;
//...
}
On update, the field annotated with @Version
will be incremented and added to the WHERE
clause, something like this:
UPDATE MYENTITY SET ..., VERSION = VERSION + 1 WHERE ((ID = ?) AND (VERSION = ?))
If the WHERE
clause fails to match a record (because the same entity has already been updated by another thread), then the persistence provider will throw an OptimisticLockException
.
Does it mean that we should declare our version field as final
No but you could consider making the setter protected as you're not supposed to call it.
Although @Pascal answer is perfectly valid, from my experience I find the code below helpful to accomplish optimistic locking:
@Entity
public class MyEntity implements Serializable {
// ...
@Version
@Column(name = "optlock", columnDefinition = "integer DEFAULT 0", nullable = false)
private long version = 0L;
// ...
}
Why? Because:
- Optimistic locking won't work if field annotated with
@Version
is accidentally set tonull
. - As this special field isn't necessarily a business version of the object, to avoid a misleading, I prefer to name such field to something like
optlock
rather thanversion
.
First point doesn't matter if application uses only JPA for inserting data into the database, as JPA vendor will enforce 0
for @version
field at creation time. But almost always plain SQL statements are also in use (at least during unit and integration testing).