Jpa - Hibernate @Version некорректно увеличивается

Я использую jpa с hibernate (3.2.7) в качестве реализации orm. У меня есть объект, который изменяется, а затем объединяется. У меня также есть @EntityListeners на этом объекте, чтобы обеспечить оценку некоторого атрибута.

Если я изменил значение перед слиянием, а затем изменил это значение в методе @PreUpdate внутри Listener, установив исходное значение, моя версия на результатах сущности увеличилась, но в версии базы данных было предыдущее значение. Я думаю, что это связано с тем, что объект не изменился, поэтому на db он не обновился, но версия на сущности была увеличена, без восстановления после флеша.

Чтобы лучше объяснить, у меня есть этот объект:

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

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

    private String myValue;

    @Version    
    private Long version ;
}

и этот прослушиватель:

public class MyListener {

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

Теперь предположим, что у меня есть объект db с этими значениями: (id = 1, myValue = 'defalut', version = 1). Я прочитал этот объект, отделил его, передал его клиенту и вернул его с помощью myValue = 'new' и выполнил операцию слияния (для прослушивания меняют myValue на "default" и поэтому результат объекта не изменен на db), flush и exit from transaction (так стремится). После этого я нахожу version = 2 на моем объекте, но version = 1 на db.

Это ошибка спящего режима? Или ошибка Jpa?

Ответ 1

Я бы сказал, что это ожидаемое поведение. В соответствии с Hibernate manual, @PreUpdate - "Выполнено перед операцией UPDATE базы данных". Итак, Hibernate уже выяснил, что он должен делать UPDATE, запустив грязную проверку и вернув ей true.

Грязная проверка не может произойти ПОСЛЕ @PreUpdate, потому что Hibernate не должен вызывать @PreUpdate, если только грязная проверка не верна. И если обновление будет запущено, версия должна быть увеличена.

Думали ли вы об использовании прослушивателя для @PrePersist вместо @PreUpdate? Я считаю, что достаточно рано в процессе, что это будет предварительная проверка, и, таким образом, иметь поведение, которое вы хотите.

Ответ 2

Для меня это выглядит как нарушение спецификации. Или, может быть, это слишком сильная формулировка, лучше сказать, что такой угловой случай недостаточно четко указан. JPA 1.0 (Hibernate 3.2.7 является одной из реализаций) спецификаций достаточно четко, когда версия должна быть обновлена:

Атрибут версии обновляется во время выполнения поставщика когда объект записывается в базу данных.

Также установка основного атрибута в вызываемом объекте вслух (JPA 2.0):

Метод обратного вызова жизненного цикла может изменять состояние взаимосвязи объект, на который он вызывается.