2011-07-19 37 views
10

使用@ElementCollection時,加載全部加載對象的多個實例。更具體地說,它爲collectionOfStrings中的每個元素加載一個實例。@ElementCollection Java持久性(Hibernate)導致加載重複實例

例如,一個MyClass的實例與collectionOfStrings.size()== 4的數據庫,加載所有MyClass值的調用將返回一個大小爲4的列表(所有相同的對象),而不僅僅是1個對象。

有沒有一種乾淨而簡單的方法來解決這個問題,或者是預期的行爲?

// 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

這是列表的正確行爲。 列表允許重複對象,這就是您需要索引列的原因。

這是可以通過休眠被映射的一般集合類型:

設置是在其中沒有項,則會出現一次以上的集合。根據我的經驗,這是最常見的永久性收集類型。

是哪些項目,可能會出現一次以上colleciton:他們是非常低效的,因爲Hibernate無法知道,如果你把它付諸表決的項目是一樣的人已經在它(假設他們是平等的),所以它必須刪除整個集合並將其從內存中重新保存。

列表是一個索引袋。索引讓hibernate知道一個特定的內存中的對象是否與一個相同的on-DB對象相同,因此不需要完整的刪除/重新插入。

地圖就像一個列表,除了索引不一定是可計算的(通常是連續的)整數,它可以是任何東西,甚至是另一個對象。

所以在你的情況下,我建議你使用Set。

+2

不幸的是,使用Set不適用於這個類,因爲它是一個有序集合,所以需要List。澄清,問題不在於List'collectionOfStrings'包含重複項,而是List'myClassInstances'包含重複項。 – HeavyE

2

這是觀察到,只有當收集預先抓取。 Hibernate將此註釋映射轉換爲外部連接查詢,這會在每個鏈接的collectionOfString元素的根元素列表中導致多個元素。

查看門票HHH-6783在Hibernate ORM問題跟蹤器中的確切問題。顯然沒有解決方案。 :-(

鏈接to the Hibernate FAQ是進入問題與外連接也在這裏提供。

我處理完全相同的問題。使用一個@ElementCollection有道理在我的情況,但不是在審查我所有數據訪問層實施的成本 要做什麼?

+0

它被標記爲HHH-6783中的一個錯誤,但它聞起來很像我的一個bug。我使用Hibernate.initialize()的LAZY,因爲在我的用例中只有少數對象被返回並且N + 1查詢可以。 –