Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jpa - Hibernate @Version incorrectly incremented

I'm using jpa with hibernate (3.2.7) as orm implementation. I have an entity being modified and then merged. I also have an @EntityListeners on this entity to ensure some attribute being valued.

If I change a value before merge, and then change back that value in @PreUpdate method inside Listener, setting the original value, my version on entity results incremented, but on database version has previous value. I think this is due to object didn't change, so on db it's not updated, but version on entity was alredy incremented without being restored after flush.

To explain better, i have this object:

@Entity
@EntityListeners({MyListener.class})
public class MyEntity {

    @Id 
    @GeneratedValue(strategy=GenerationType.AUTO)   
    private Long id;

    private String myValue;

    @Version    
    private Long version ;
}

and this Listener:

public class MyListener {

    @PreUpdate
    public void preUpdate(MyEntity ua) { 
        ua.setMyValue("default");
    }
}

Now suppose i have on db an object with these values: (id=1, myValue='defalut', version=1). I read this object, detach, pass it to client and get it back with myValue='new' and perform a merge operation (listener change myValue to 'default' and so object result unmodified to db), flush and exit from transaction (so is committed). After that i find version=2 on my object, but version=1 on db.

Is that an hibernate bug? Or a Jpa bug?

like image 300
zime Avatar asked Sep 30 '11 10:09

zime


2 Answers

I would say this is an expected behavior. According to the Hibernate manual, @PreUpdate is "Executed before the database UPDATE operation.". So, Hibernate has already figured out it has to do UPDATE by running the dirty check, and having it return true.

The dirty check can't happen AFTER @PreUpdate, because Hibernate shouldn't call @PreUpdate unless the dirty check is true. And if an update is going to be run, version should be incremented.

Have you thought about using a listener for @PrePersist, instead of @PreUpdate? I believe that's early enough in the process that it would be pre-dirty check, and thus have the behavior that you want.

like image 110
sharakan Avatar answered Nov 01 '22 16:11

sharakan


For me it looks like specification violation. Or maybe that is too strong wording, better probably to say that such a corner case is not clearly enough specified. JPA 1.0 (Hibernate 3.2.7 being one of the implementations) specification spells quite clearly when version should be updated:

The version attribute is updated by the persistence provider runtime when the object is written to the database.

Also setting basic attribute in invoked entity is aloud (JPA 2.0):

A lifecycle callback method may modify the non-relationship state of the entity on which it is invoked.

like image 1
Mikko Maunu Avatar answered Nov 01 '22 16:11

Mikko Maunu