18

Collectionエンティティのを返す名前付きクエリがあります。

これらのエンティティには、@PreUpdate注釈付きのメソッドがあります。このメソッドは、中に呼び出されquery.getResultList()ます。このため、エンティティは永続コンテキスト内で変更されます。つまり、トランザクションのコミット時に、エンティティはデータベースに書き戻されます。

どうしてこれなの?@PreUpdateJPA 2.0仕様では、クエリの実行によって呼び出される必要があることを明示的に言及していません。

4

2 に答える 2

11

仕様によると:

PreUpdateコールバックとPostUpdateコールバックは、それぞれエンティティデータへのデータベース更新操作の前後に発生します。これらのデータベース操作は、エンティティの状態が更新されたときに発生する場合と、状態がデータベースにフラッシュされたときに発生する場合があります(トランザクションの終了時)。

この場合、呼び出しはaをquery.getResultList()トリガーしem.flush()て、クエリに現在のEntityManagerセッションからの変更を含めることができるようにします。em.flush()すべての変更をデータベースにプッシュします(すべてのUPDATE、INSERT呼び出しを行います)。BeforeUPDATEがJDBCを介して送信さ@PreUpdateれると、対応するフックが呼び出されます。

于 2013-05-01T20:03:27.937 に答える
2

これは、フォローアップコードを含むrzymekの回答からの私のコメントです。

クエリが呼び出されるたびにEntityManagerがフラッシュされるように聞こえたため、OPが抱えていた問題を再現しようとしました。しかし、そうではありません。@PostUpdateメソッドは、私が知る限り、データベースに実際に変更が加えられた場合にのみ呼び出されます。DBにまだフラッシュされていないEntityManagerで変更を加えた場合、query.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);
        }
于 2014-12-06T11:59:39.417 に答える