5

Invalidating JPA EntityManager sessionで説明されているような問題に直面しています:

問題: 古いデータの取得

別のアプリケーションによって同時に変更される SQL データベースで JPQL クエリを実行しています。Tomcat で動作する JSF+Spring+EclipseLink を使用しています。

JPQL クエリを実行するクラスはシングルトン Spring Bean であり、注入された を使用しますEntityManager

@Component
public class DataRepository{
    @PersistenceContext
    private EntityManager entityManager;
    public List<MyDTO> getStuff(long id) {
        String jpqlQuery ="SELECT new MyDTO([...])";
        TypedQuery<MyDTO> query = entityManager.createQuery(jpqlQuery,MyDTO.class);
        return query.getResultList();
    }
[...]

(コードの言い換え)。

問題は、このコードがデータベースで直接実行された変更を認識しないことです。これらの変更は、Tomcat インスタンスが再起動された場合にのみ表示されます。

私たちが試したこと

EntityManagerリンクされた質問で説明されているように、この動作は に関連付けられた第 1 レベルのキャッシュが原因であると想定しています。2 つの解決策が見つかりました。

  • 呼び出すentityManager.clear()前に呼び出しますcreateQuery(これはリンクされた質問で提案されています)
  • EntityManagerFactor(を使用して)を注入し、クエリごと@PersistenceUnitに新しいを作成して閉じますEntityManager

どちらのソリューションも、私たちが望むことを行います - 新しいデータを取得します。

質問:

  • これら2つの解決策は正しいですか?どちらの方がよいですか?
  • 特に、注入された EntityManager を安全に呼び出すことができますentityManager.clear()か、それとも (同じクラスまたは別のクラスで) 注入された EntityManager も使用する他のコードに何らかの影響を与えますか?
  • 別のより良いアプローチはありますか?キャッシュを更新したい、または更新したいことをどうにかして宣言できますEntityManagerか?

これはかなり一般的な問題だと思います(複数のアプリがデータベースを共有するたびに発生するため)ので、簡単な解決策があるはずだと思いました...

4

4 に答える 4

5

問題が解決したようです。

実際には次の 2 つの問題がありました。

  1. 問題のデバッグ中に、すべてのクエリに新しい EntityManager を使用しない場合がありました。同じ EntityManager を使用して再利用する場合、エンティティは一度取得されると明らかに更新されませEntityManager.refresh()ん (を使用して明示的に更新されない限り)。これは、エンティティが読み込まれると、EntityManager の永続コンテキスト (別名、第 1 レベルのキャッシュ) に格納されるためと思われます。JPA 仕様では、同じエンティティに対する後続のクエリが同じオブジェクト インスタンスを返す必要があるため、これを行う必要があります。つまり、同じ EntityManager を使用している限り、明示的に更新しない限り、古いデータが取得されます。

  2. 各クエリに新しい EntityManager を使用した場合、通常はうまくいきました。ただし、特定の JPQL クエリ (コンストラクター式と JOIN FETCH を含む) に対して古いデータが返されるEclipseLink のバグに遭遇しました。

短縮版

プログラムの外部から変更されたデータベースから常に新しいデータを取得したい場合は、

  • 新しいデータが必要なクエリごとに新しい EntityManager を使用します
  • 2 番目のレベルのキャッシュを無効にします ( <shared-cache-mode>NONE</shared-cache-mode>persistence.xml 内)。
于 2013-01-22T15:33:36.723 に答える
2
  1. 新しい を作成することEntityManagerは正しいですが、それ以外は正しくありません (以下を参照)。

  2. 呼び出しEntityManager#clear()は、シングルスレッド システム以外では非常に危険です。

    ...すべての管理対象エンティティが分離されます...

    したがって、「あなたの」スレッドがエンティティマネージャーをクリアしている間に、1 つのスレッドが接続されたエンティティで動作すると、重大な副作用が発生します。

  3. うーん、わかりにくい。アプリケーションの外部で変更されたエンティティの数が少ない場合は、データソースとJdbcTemplateそれぞれの操作を直接操作します。

于 2013-01-07T19:36:30.610 に答える
1

EclipseLink で共有キャッシュを無効にするには、以下を参照してください。

http://wiki.eclipse.org/EclipseLink/FAQ/How_to_disable_the_shared_cache%3F

于 2013-01-08T14:45:58.820 に答える