为什么在查询期间调用JPA @ PreUpdate-annotated方法?

时间:2012-08-23 17:58:04

标签: jpa

我有一个返回Collection个实体的命名查询。

这些实体对它们有一个@PreUpdate - 注释方法。在query.getResultList()期间调用此方法。因此,实体在持久化上下文中被更改,这意味着在事务提交时,实体将被写回数据库。

这是为什么? JPA 2.0规范没有明确提到查询执行应该调用@PreUpdate

2 个答案:

答案 0 :(得分:10)

规范说:

  

PreUpdate和PostUpdate回调发生在之前和之后   数据库更新操作分别对实体数据。这些数据库   操作可以在实体状态更新时发生或者它们发生   可能在状态被刷新到数据库时发生(可能是   在交易结束时)。

在这种情况下,调用query.getResultList()会触发em.flush(),以便查询可以包含从当前EntityManager会话更改的内容。 em.flush()将所有更改推送到数据库(进行所有UPDATE,INSERT调用)。在通过JDBC UPDATE发送@PreUpdate之前,会调用相应的挂钩。

答案 1 :(得分:2)

这只是我对rzymek的答案中的一些跟进代码的评论:

我试图重现OP的问题,因为它听起来像每次调用查询时都会刷新EntityManager。但事实并非如此。 @PostUpdate方法仅在我对数据库进行实际更改时才会调用。如果您使用尚未刷新到DB查询的EntityManager进行了更改.getResultList将触发对DB的刷新,这是您应该期望的行为。

        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);
        }