2
votes

I am trying to use optimistic locking using the version field and no exception is being thrown when I call the save from the jpa repository. I am new to Spring and hibernate and I am worried that I am setting it up incorrectly.
The libraries i am using are: hibernate4-maven-plugin version 1.0.2 hibernate-jpa02.0 1.0.1 spring-data-jpa version 1.3.4

So my entity is set up like this:

@Entity
public class MyEntity
{
    @Id
    protected Long id;

    @Version
    protected Long version;

    protected String name;

    public Long getVersion()
    {
      return version;
    }

    public void setVersion(Long version)
    {
      this.version = version;
    }

    public Long getVersion()
{
      return version;
}

public void setVersion(Long version)
    {
       this.version = version;
    }

public Long getId()
    {
      return id;
    }

public void setId(Long id)
    {
      this.id = id;
    }

    public String getName()
    {
    return name;
    }

public void setName(Long id)
    {
       this.name = name;
    }
 }

I pass the version through to the client through my dto and pass it back when i do a save in my MyEntityStoreDao:

 @Repository
    public class MyEntityStoreDao extends BaseDao<MyEntityStoreDao>
    {

       private RepositoryManager myRepoManager;

       @Autowired
       public void setMyRepo(MyEntityRepository myRepo)
       {
           this.myRepo = myRepo;
       }

       public MyEntity save(MyEntityDTO dtoToUpdate)
       {
           Session session = this.Session();
           MyEntity myEntity = new MyEntity();

           if(dtoToUpdate.getId() > 0) {
             myEntity = (MyEntity) session.get(MyEntity.class, dtoToUpdate.getId())  
           }      

           myEntity.setName(dtoToUpdate.getName());

           MyEntity result = this.myRepo.save(myEntity); 

           this.repositoryManager.flush(myRepo);

       }
    }

The repositoryManager is in the BaseDao and is using the org.springframework.data.jpa.repository.JpaRepository.

The version is being updated correctly and incrementing. But when i do an update, I expect when the version being passed through from the DTO to save in the MyEntityStoreDao to not match what is in the database, it would throw a StaleStateException or OptmisticLockingException.

I checked and the versions do not match but the save still occurs. Any help on why this is happening? Thanks

2
Where is your update code? If you are updating an object, please call saveOrUpdate(object) method on the repoZeus
You're not changing the version yourself are you? That is management the JPA provider should be doing. The proper way to test this is to create two threads, each fetching and updating the same entity in their own unique transaction at the same time; one of them should fail when saving. This is easy to accomplish by putting a sleep of a few seconds in the thread before committing.Gimby
@Zeus - The update code is where the jpa repository takes care of the save.Lumpy
@Gimby The version is just passed through without touching that value. MyEntityDTO has a version field on it that is passed through to the client and then back. And when the session goes and gets the entity from the schema, the new version value is pulling the right one. I did test it and the versions were different when it tries the save and still succeeds.Lumpy
i am confused. dtoToUpdate is MyEntityDTO type . and as per your comments you are saving this MyEntittyDTO entity . you haven't changed version in MyEntityDao. What is "MyEntity " here ? did you compare version values between these two ? seems two different entity / two different tables ?.. help me to understand the code above . i dont see a reason for MyEntity there . are you doing any thing with that..Mani

2 Answers

1
votes

Turn On sql logging by show-sql=true and see if the update query has the required where clause

where version = ?

If such where clause is missing then you need to add annotation @org.hibernate.annotations.Entity(dynamicUpdate = true)

0
votes

With your updated Code

 MyEntity myEntity = new MyEntity(); // You dont need to initalize

           if(dtoToUpdate.getId() > 0) {
             myEntity = (MyEntity) session.get(MyEntity.class, dtoToUpdate.getId())  
           }      

           myEntity.setName(dtoToUpdate.getName());

           MyEntity result = this.myRepo.save(myEntity); 

you are trying to save the myEntity , Which has recent information (correct version) from database. So you will not get any error. if you want to produce the error please do the following..

public MyEntity save(MyEntityDTO dtoToUpdate)
   {
       Session session = this.Session();
       MyEntity myEntityV1 = null;
       MyEntity myEntityV2 = null;

      // Getting v1 and V2. at this time both V1 & V2 will have same version ( assume version as 5)
       if(dtoToUpdate.getId() > 0) {
         myEntityV1 = (MyEntity) session.get(MyEntity.class, dtoToUpdate.getId())  ;
         myEntityV2 = (MyEntity) session.get(MyEntity.class, dtoToUpdate.getId())  ;
       }      

       myEntityV1.setName(dtoToUpdate.getName());

       // Saving V1 will reflect the increase in version  ( actual row will be version of 6)
       MyEntity result = this.myRepo.save(myEntityV1);   

       myEntityV2.setName("some changes"); // change some in V2instance. So that hibernate/Jpa will capture the change
       this.myRepo.save(myEntityV2);    // You will get exception. because the v2 has version as 5. but the row was updated .

       this.repositoryManager.flush(myRepo);

   }

So basically when you update the entity in Db, if the entity version (variable in object) is not equal to the version ( field in the table) in database ( before this update) it will throw exception