Почему во время запроса вызывается метод 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);
        }