0

JOIN FETCH と EAGER の関係で問題に直面しています。

次のエンティティ関係があります。

エンティティ A は抽象エンティティ E を拡張します。

抽象エンティティ E は、フェッチ タイプが EAGERのエンティティ C と、1 対 1 の双方向関係を持っています。

エンティティ A は、エンティティ B と oneToMany の関係にあります。

エンティティ B は抽象エンティティ E を拡張します (したがって、C との oneToOne 関係も持ちます)

エンティティ C は抽象的なエンティティ E と逆の関係にある

次のような単純なnamedQueryを実行している間

SELECT a FROM A a WHERE a.key = :key

パラメータ「キー」が文字列型の場合、問題はありません。取得したエンティティ A からサブ エンティティ B にアクセスすると、想定どおりにサブ リクエストが実行されます。

しかし、namedQuery に JOIN FETCH を追加すると、次のようになります。

SELECT a FROM A a JOIN FETCH a.entitiesB WHERE a.key = :key

次のエラー スタック トレースを取得します。

javax.ejb.EJBTransactionRolledbackException: The transaction has been marked rollback only because the bean encountered a non-application exception :javax.ejb.EJBTransactionRolledbackException : The transaction has been marked rollback only because the bean encountered a non-application exception :org.apache.openjpa.persistence.ArgumentException : The specified parameter of type "class org.apache.openjpa.util.LongId" is not a valid query parameter.
at org.apache.openejb.core.ivm.BaseEjbProxyHandler.convertException(BaseEjbProxyHandler.java:345)
at org.apache.openejb.core.ivm.BaseEjbProxyHandler.invoke(BaseEjbProxyHandler.java:283)
... 
Caused by: <openjpa-2.2.0-r422266:1244990 nonfatal user error> org.apache.openjpa.persistence.ArgumentException: The specified parameter of type "class org.apache.openjpa.util.LongId" is not a valid query parameter.
FailedObject: SELECT relation FROM Relation relation JOIN FETCH relation.accounts WHERE relation.key = :key [java.lang.String]
at org.apache.openjpa.jdbc.sql.DBDictionary.setUnknown(DBDictionary.java:1458)
at org.apache.openjpa.jdbc.sql.SQLBuffer.setParameters(SQLBuffer.java:544)
at org.apache.openjpa.jdbc.sql.SQLBuffer.prepareStatement(SQLBuffer.java:453)
at org.apache.openjpa.jdbc.sql.SQLBuffer.prepareStatement(SQLBuffer.java:429)
at org.apache.openjpa.jdbc.sql.SelectImpl.prepareStatement(SelectImpl.java:479)
... 

EAGER 関係を LAZY に変更すると、このエラーは発生しなくなります。だから問題は何ですか ?

編集 1: LAZY フェッチ タイプで oneToOne 関係を維持し、namedQuery に JOIN FETCH a.entityC を直接追加すると、同じエラーが発生します。

編集 2: エンティティ C とのリレーション oneToOne を abstractEntity クラスから削除し、このリレーションを EntityA および EntityB に直接追加し (さらに、エンティティ C を A & B と逆のリレーションを持つように変更します)、この oneToOne リレーションを EAGER として保持します => 問題ありません。この問題は、この共有関係を抽象エンティティ クラスに持つことから発生しているように見えますが、なぜでしょうか?

これは問題を示すコード例です。

エンティティ A

@Entity
@Table(name = "TABLE_A")
@Access(AccessType.FIELD)
@NamedQueries({
   @NamedQuery(name = "findADetails", query = "SELECT a FROM A a WHERE a.customKey.key     = :customKey")})
public class A extends abstractEntity {

   @Embedded
   private CustomEmbeddedKey customKey;

   @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "entityA")
   private List<B> bEntities;

   ...
}

エンティティ B

@Entity
@Table(name = "TABLE_B")
@Access(AccessType.FIELD)
public class B extends abstractEntity {

   @ManyToOne(fetch = FetchType.LAZY, targetEntity = A.class)
   @JoinColumn(name = "FK_A_ID")
   private A entityA;

   ...
}

抽象エンティティ クラス

@Entity
@Access(AccessType.FIELD)
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@SequenceGenerator(name = "TOTO_ID_SEQ", sequenceName = "TOTO_ID_SEQ", initialValue = 1, allocationSize = 1)
public abstract class abstractEntity {

   @Id
   @Column(name = "ID")
   @GeneratedValue(generator = "TOTO_ID_SEQ", strategy = GenerationType.SEQUENCE)
   private Long id;

   @OneToOne(mappedBy = "...", fetch = FetchType.EAGER)
   private C anotherEntity;

   @OneToMany(mappedBy = "...", fetch = FetchType.LAZY)
   private List<D> anotherEntities;

   ...

}

埋め込みキー

@Embeddable
@Access(AccessType.FIELD)
public class CustomEmbeddedKey {

   @Column(name = "...", length = ...)
   private String key;

   ...
}

ダオ サンプル

TypedQuery<A> query = createNamedQuery("findADetails", A.class);
  query.setParameter("customKey", queryParam);
  List<A> aEntitiesFound = query.getResultList();

よろしくお願いします。

4

1 に答える 1

1

私は問題を見つけたと思います。Eager Fetching Considerations and Limitations に関するドキュメントを参照すると、openJPA で実際にはサポートされていないマッピングが原因です。

最後に、私には2つの解決策があります:


解決策 1 (簡単なもの):

  • 抽象エンティティ クラスのフェッチ タイプ EAGER を LAZY に変更します。
  • 取得グルー​​プを追加し、関連するそれぞれの名前付きクエリまたはコードを更新します

利点:

  • 迅速な解決策であり、既存のコードに大きな影響を与えません。

短所:

  • getter ごとに遅延ロードされるため、新しい LAZY フィールドの使用と関連するクエリの更新について調査する必要があります (更新された namedQueries からでない場合)。
  • Junit および動的拡張を使用してテストを実行すると、そのようなマッピング (抽象化および TABLE_PER_CLASS 継承タイプ) を使用して namedQueries で JOIN FETCH を実行しているときに NPE がスローされます。

解決策 2:マッピングを変更する

  • 抽象エンティティから、継承の使用を削除し (実際にはビジネスでの使用はありません)、@MappedSuperClass に置き換えます (したがって、@Entity と @Inheritance を削除します)。
  • インターフェイスを追加し、EAGER プロパティのゲッター / セッターを定義する
  • このインターフェイスを抽象エンティティ クラスに実装して、子クラスが getter/setter (および関連するプロパティとマッピング) を実装する必要があるようにします。
  • これらの EAGER オブジェクトのターゲット エンティティへの逆関係のタイプを、抽象クラス タイプから新しいインターフェイス タイプに変更します。
  • 最後に、EAGER プロパティのマッピング、ゲッター、セッターの実装を抽象エンティティ クラスからすべての子エンティティ クラスに移動します。

利点:

  • 例外はもうありません (古いマッピングと動的クラス拡張による NPE もありません)
  • 古いマッピング スタイルに対する openJPA の制限により、さらなる例外を回避する可能性が高くなるため、より強力なソリューション

短所:

  • その他の変更
  • ターゲットエンティティクラスへの抽象クラスタイプの代わりにインターフェースがあるため、開発者にとっては読みにくいかもしれません
于 2013-07-29T12:46:37.457 に答える