2011-07-19 1 views
10

@ ElementCollectionを使用すると、load allはオブジェクトの複数インスタンスをロードしています。具体的には、collectionOfStringsの各要素に対して1つのインスタンスをロードしています。@ ElementCollection Java Persistence(Hibernate)により重複インスタンスがロードされる

たとえば、collectionOfStrings.size()== 4のMyClassの単一インスタンスを持つデータベースでは、すべてのMyClass値をロードする呼び出しは、ただ1つのオブジェクトではなくサイズ4(すべて同じオブジェクト)のリストを返します。

これを解決するクリーンで簡単な方法はありますか?

// Parent class is a @MappedSuperclass which may or may not be relevant to the issue 
@Entity 
public class MyClass extends ParentClass { 

    @Id 
    @GeneratedValue(strategy = GenerationType.AUTO) 
    private long id; 

    @ElementCollection(fetch=FetchType.EAGER) 
    @IndexColumn(name="indexColumn") 
    private List<String> collectionOfStrings; 

    // other instance variables, constructors, getters, setters, toString, hashcode and equals 
} 

public class MyClassDAO_Hibernate extends GenericHibernateDAO<MyClass, Long> implements MyClassDAO { 

    @Override 
    public List<MyClass> loadAll() { 
     List<MyClass> entityList = null; 
     Session session = getSession(); 
     Transaction trans = session.beginTransaction(); 
     entityList = findByCriteria(session); 
     trans.commit(); 
     return entityList; 
    } 

} 

protected List<T> findByCriteria(Session session, Criterion... criterion) { 
    Criteria crit = session.createCriteria(getPersistentClass()); 
    for (Criterion c : criterion) { 
     crit.add(c); 
    } 
    return crit.list(); 
} 

MyClassDAO myClassDAO = new MyClassDAO_Hibernate(); // in reality, implementation type is determined with a Factory 
... 
List<MyClass> myClassInstances = myClassDAO.loadAll(); 

おかげで、 HeavyE

編集:findByCriteria呼び出しを追加しました。

+0

どのようにエンティティを読み込みますか? – axtavt

+0

axtavt、私は私が本来含んでいないfindByCriteriaメソッドを追加しました。 – HeavyE

答えて

8

私はそれがバグまたは正当な行動であるかどうかわからないんだけど、それはDISTINCT_ROOT_ENTITY結果変圧器を適用することにより、固定することができます。

protected List<T> findByCriteria(Session session, Criterion... criterion) { 
    Criteria crit = session.createCriteria(getPersistentClass()); 
    for (Criterion c : criterion) { 
     crit.add(c); 
    } 
    crit.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY); 
    return crit.list(); 
} 
+0

完全に働いた - ありがとう! – HeavyE

2

これはリストの正しい動作です。 Listはオブジェクトの重複を許し、インデックス化された列が必要な理由です。

これは、Hibernateによってマッピングすることができる一般的なコレクション型である:

セットにはアイテムが複数回発生しないコレクションです。私の経験上、これは最も一般的な永続的なコレクション型です。

バッグは、アイテムが複数回発生する可能性のあるcollecitonです:あなたがそれに入れアイテムがすでにその中のものと同じである場合、Hibernateはそう、(それらが等しいと仮定して)言うことができないので、彼らは非常に非効率的ですそれはコレクション全体を削除し、それをメモリから再保存する必要があります。

リストは索引付きの袋です。インデックスは、特定のメモリ内オブジェクトがDBオブジェクト上の等しいオブジェクトと同じであるかどうかをHibernateに知らせるので、完全な削除/再挿入は必要ありません。

地図は、インデックスが計算可能(通常はシーケンシャル)の整数である必要はありません除いて、それは何でも、でも他のオブジェクトにすることができ、単にリストのようなものです。

あなたの場合は、セットを使用することをお勧めします。

+2

残念ながら、Setを使用することはこのクラスでは機能しません。リストは順序付けされたコレクションなので必須です。説明を明確にするため、リスト 'collectionOfStrings'に重複が含まれていても、リスト 'myClassInstances'に重複が含まれているという問題はありません。 – HeavyE

2

これは、コレクションは即時にフェッチされた場合にのみ観察されます。 Hibernateは、このアノテーションマッピングを外部結合クエリに変換します。これにより、リンクされた各collectionOfString要素のルート要素リストに複数の要素が存在します。

この正確な問題については、Hibernate ORM発行トラッカーのチケットHHH-6783を参照してください。そして明らかに解決策はありません。 :-(

もここで提供される外部結合の問題に入りリンクto the Hibernate FAQ

私は正確に同じ問題を扱っています。@ElementCollectionは、私の場合には理にかなって使用してではなく、時私のすべてのデータアクセスレイヤの実装を見直すコスト。 何をするべきですか?

+0

HHH-6783ではバグではないとマークされていますが、バグのような匂いがします。私はHibernate.initialize()を使用してLAZYに頼っていました。私のユースケースではほとんどオブジェクトが返されず、N + 1のクエリが正常です。 –

関連する問題