我有一个返回Collection
实体的命名查询。
这些实体有一个@PreUpdate
-annotated 方法。此方法在 期间调用query.getResultList()
。因此,实体在持久性上下文中发生了变化,这意味着在事务提交时,实体被写回数据库。
为什么是这样?JPA 2.0 规范没有明确提到@PreUpdate
应该由查询执行调用。
我有一个返回Collection
实体的命名查询。
这些实体有一个@PreUpdate
-annotated 方法。此方法在 期间调用query.getResultList()
。因此,实体在持久性上下文中发生了变化,这意味着在事务提交时,实体被写回数据库。
为什么是这样?JPA 2.0 规范没有明确提到@PreUpdate
应该由查询执行调用。
规范说:
PreUpdate 和 PostUpdate 回调分别发生在对实体数据进行数据库更新操作之前和之后。这些数据库操作可能发生在更新实体状态时,也可能发生在状态刷新到数据库时(可能在事务结束时)。
在这种情况下,调用query.getResultList()
会触发 aem.flush()
以便查询可以包括从当前 EntityManager 会话中更改的内容。em.flush()
将所有更改推送到数据库(进行所有 UPDATE、INSERT 调用)。在UPDATE
通过 JDBC 发送之前@PreUpdate
调用相应的钩子。
这只是我对 rzymek 的回答以及一些后续代码的评论:
我试图重现 OP 的问题,因为听起来每次调用查询时 EntityManager 都会被刷新。但事实并非如此。据我所知,只有在对数据库进行实际更改时才会调用 @PostUpdate 方法。如果您对尚未刷新到数据库的 EntityManager 进行了更改,则 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/questions/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);
}