Java - JPA - @Version Annotation

Java - JPA - @Version annotation

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.

@Version annotation in spring

@Version is intended for optimistic locking and should not be used for this.

As Taylor commented you can use Hibernate Envers for something really similar. It will result in a separate table containing all your versions, while the main table that you map your entity to still has only the current version.

If you want all these entities to be actually in the same table you should just make the existing id plus an additional version field the id and then copy the entity and update the version before changing it. Version field here means a normal probably numeric field that you use as version it must not be annotated with @Version.

If you really want to abuse the @Version attribute you could create a trigger on the underlying table that creates a new row on every update.

Spring Boot Data JPA @Version Returns 0 on Create but is 1 in the database

  • INCIDENT_VER_NUM field should not have autoincrement column but you seems to have an autoincrecment column as it is managed and incremented by JPA

  • If you can't remove autocrement, try this option and it could work. But I have used this option so far other fields that are generated by database not for @Version

    @Version
@Column(name = "INCIDENT_VER_NUM", insertable = false, updatable = false)
@org.hibernate.annotations.Generated(value = GenerationTime.ALWAYS)
private Long version;

Using @Version in spring-data project

It's been a while since I posted this question but I've figured it out. I'll explain what I did so that it might be helpful to someone else.

The annotation @Version is a javax.persistence interface and not the spring-data rest jpa framework as i mentioned earlier.

If you want to make use of @Version you need to create an version field in your domain object like so:

@Version
@Column(name = "VERSION")
private long version;

If you're using hibernate it will automatically pickup the annotation and it will create a "version" column in your (in my case MySql) table. Every time a record gets updated, hibernate will increment the counter with 1.

Now why is this something you want? Well the reason why you might wanna use this is because it decreases the chance that your clients are working with stale data. Whenever a client retrieves information from you a version is provided with the data he requested. e.g.

{                       <-- School entity -->
"id": 1,
"version": 0,
"name": "De regenboog",
"street": "Plantaanstraat",
"number": "2",
"zipCode": "1234AS",
"city": "Amsterdam"
}

Now if a client wants to change some information about this specific record it sends the new information along with the version value. In this case let's change the name of the school.

 {                       <-- School entity -->
"id": 1,
"version": 0,
"name": "Stackoverflow",
"street": "Plantaanstraat",
"number": "2",
"zipCode": "1234AS",
"city": "Amsterdam"
}

Hibernate comes up with a query with your information and adds an extra 'where' clause to check the version. update .... where id = 1 and version = 0. Now if the row is updated it means you provided the right version and no one else has changed that specific information between the time you requested the information, changed it and sent it back. Nice right?

Now what if the row isn't updated? It means someone else updated that row while you were taking a quick bathroom break after you requested the information. It means your version is outdated! What needs to happen now is really use case specific so I won't go into details about that

Hope someone can use this piece of information!

How does JPA @Version annotation works when you have multiple entities

Optimistic locking works for individual table rows only. In this particular case, the entity C can be updated successfully since the second transaction does not modify it.

If you want to create a conflict here, then you must use either of the following optimistic lock requests:

  • OPTIMISTIC_FORCE_INCREMENT
  • PESSIMISTIC_FORCE_INCREMENT

This way, in the second transaction, whenever you update the entity A, you'll also trigger a version increment in C.

How to set JPA @Version marked field to explicit value

According to the hibernate documentation:

Your application is forbidden from altering the version number set by Hibernate. To artificially increase the version number, see the documentation for properties LockModeType.OPTIMISTIC_FORCE_INCREMENT or LockModeType.PESSIMISTIC_FORCE_INCREMENT check in the Hibernate Entity Manager reference documentation.

If the version number is generated by the database, such as a trigger, use the annotation @org.hibernate.annotations.Generated(GenerationTime.ALWAYS) on the version attribute.

JPA @Version field doesn't get incremented

The entityManager.persist() method is meant to be used only for new entities that have never been persisted before.

Because you are fetching an entity you don't need to call persist or merge anyway. The dirty checking will do the update on your behalf.

The commit will trigger the flush anyway so you should see the update.

Make sure you use the javax.persistence.Version annotation and not from another package (spring data or something similar).

How to update the JPA/Hibernate @Version field in a Spring Data JPA @Modifying @Query query?

You can add u.version=u.version+1 to the query. The updated @Querys look like this:

import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;

public interface UserRepository extends CrudRepository<User, Long> {

@Modifying
@Query("UPDATE User u SET u.version=u.version+1, u.active = false WHERE u.id = ?1")
void deactivate(Long id);

@Modifying
@Query("UPDATE User u SET u.version=u.version+1, u.active = false WHERE u.id IN :ids")
void deactivateAll(@Param("ids") Long... ids);

@Modifying
@Query("UPDATE User u SET u.version=u.version+1, u.active = false WHERE u.id IN :ids")
void deactivateAll(@Param("ids") Iterable<Long> ids);

}

There may be caveats using this. E.g. this will obviously not fail with an OptimisticLockException like updates issued by the EntityManager do (as described here).



Related Topics



Leave a reply



Submit