Почему во время запроса вызывается метод JPA @PreUpdate-annotated?
У меня есть именованный запрос, который возвращает Collection
объектов.
Эти объекты имеют на них @PreUpdate
-номененный метод. Этот метод вызывается во время query.getResultList()
. Из-за этого сущность изменяется в контексте персистентности, что означает, что при совершении транзакции объект записывается обратно в базу данных.
Почему это? Спецификация JPA 2.0 явно не упоминает, что @PreUpdate
должен быть вызван выполнением запроса.
Ответы
Ответ 1
В спецификации указано:
Обратные вызовы PreUpdate и PostUpdate происходят до и после операции обновления базы данных с данными сущностей соответственно. Эта база данных операции могут произойти в момент обновления состояния объекта или может произойти в момент, когда состояние сброшено в базу данных (что может быть в конце транзакции).
В этом случае вызов query.getResultList()
запускает a em.flush()
, чтобы запрос мог включать изменения из текущего сеанса EntityManager. em.flush()
выталкивает все изменения в базу данных (делает все вызовы UPDATE, INSERT). Прежде чем UPDATE
отправляется через JDBC @PreUpdate
, вызываются соответствующие перехватчики.
Ответ 2
Это только мой комментарий от ответа rzymek с некоторым последующим кодом:
Я попытался воспроизвести проблему OP, потому что это звучало так, как EntityManager будет очищаться каждый раз, когда вызывается запрос. Но это не так. Насколько мне известно, методы @PostUpdate вызывается только тогда, когда фактическое изменение выполняется в базе данных. Если вы внесли изменения в EntityManager, который еще не покраснел в DB query.getResultList вызовет сброс в БД, который является поведением, которое следует ожидать.
Place valinorDb = em.find(Place.class, valinorId);
// this should not trigger an PostUpdate and doesn't
// TODO: unit-testify this
em.merge(valinorDb);
valinorDb.setName("Valinor123");
valinorDb.setName("Valinor");
// this shouldn't trigger an PostUpdate because the Data is the same as in the beginning and doesn't
em.merge(valinorDb);
{
// this is done to test the behaviour of PostUpdate because of
// this:
// http://stackoverflow.com/info/12097485/why-does-a-jpa-preupdate-annotated-method-get-called-during-a-query
//
// this was tested by hand, but should maybe changed into a unit
// test? PostUpdate will only get called when there is an actual
// change present (at least for Hibernate & EclipseLink) so we
// should be fine
// to use PostUpdate for automatically updating our index
// this doesn't trigger a flush as well as the merge didn't even trigger one
Place place = (Place) em.createQuery("SELECT a FROM Place a")
.getResultList().get(0);
Sorcerer newSorcerer = new Sorcerer();
newSorcerer.setName("Odalbort the Unknown");
place.getSorcerers().add(newSorcerer);
//this WILL trigger an PostUpdate as the underlying data actually has changed.
place = (Place) em.createQuery("SELECT a FROM Place a")
.getResultList().get(0);
}