2014-01-21 83 views
1

任何人都可以幫我解決一個我認爲簡單的JPA問題。我試圖在我的JPA持久性框架中編寫通用的惰性負載管理器,因此應用程序堆棧中較高的層可以訪問延遲加載的數據,而無需處理具體細節。重新連接實體以延遲加載集合(JPA /休眠)

我有一個延遲加載經理:

public class JpaLazyLoader extends AbstractJpaDAO<Void> implements LazyLoader 
{ 

    public JpaLazyLoader() 
    { 
    super(void.class); 
    } 

    @Transactional(readOnly=true) 
    public <T,E> T get(ILazyGetter<T,E> p_getter) throws Exception { 
    // reattach the object to the session 

    E l_entity = getEntityManager().merge(p_getter.getEntity()); 

    // return the getter data 
    return p_getter.get(l_entity); 
    } 
} 

懶吸氣是這樣的:

public interface ILazyGetter<T,E> extends Serializable 
{ 
    public E getEntity(); 
    public T get(E p_entity) throws Exception; 
} 

這個想法是,它會像這樣使用:

  return m_lazyLoader.get(new ILazyGetter<Collection<Child>, Parent>() { 

      private static final long serialVersionUID = 1L; 

      public Parent getEntity() { 
       return getValue(); // get the parent object from somewhere 
      } 

      public Collection<Child> get(Parent p_entity) throws Exception { 
       // children are a lazy-loaded Set<Child> 
       return p_entity.getChildren(); 
      } 
      }); 

Parent中的註釋是這樣的:

@Entity(name="parent") 
public class Parent implements Serializable { 

    private static final long serialVersionUID = 1L; 

    .... 

    @Id 
    @Column(name="id") 
    @GeneratedValue(strategy=GenerationType.AUTO) 
    protected Long id; 

    @OneToMany 
    @JoinTable 
    (
     name="parent_child_associations", 
     joinColumns={ @JoinColumn(name="parent_id", referencedColumnName="id") }, 
     inverseJoinColumns={ @JoinColumn(name="child_id", referencedColumnName="id", unique=true) } 
) 
    protected Set<Child> children; 

}

父對象裝載在不同的事務,然後分離。我希望將父母重新連接到另一個會話(在@transactional位內)是件微不足道的事,但我無法讓它工作。我已經嘗試過樂觀/悲觀/合併之前和之後沒有鎖定,但似乎沒有工作。

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: test.Parent.children, could not initialize proxy - no Session 
at  org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:566) 
at  org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:186) 
at  org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:545) 
at  org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:124) 
at org.hibernate.collection.internal.PersistentSet.iterator(PersistentSet.java:180) 

這是錯誤的方式去做事嗎?如果是這樣,那麼「正確」的方式是什麼?如果不是,我做錯了什麼?

感謝所有幫助

+2

顯然,你做成功獲取集合,但它初始化?似乎你退還未打開的包,當你嘗試迭代那個包時,它會嘗試打開它,給你例外。 當您迭代集合時是否有打開的會話?如果是這樣,你可以嘗試調用Hibernate.initialize()? –

+0

是的,這正是我出錯的地方。 - 我不想將我的JPA綁定到Hibernate,所以我確保集合被初始化。見下面的評論。 – fancyplants

回答

1

OK以及感謝安德烈我找到了工作。

我只是沒有初始化集合對象,而是我只是調用parent.getChildren(),它只返回代理對象而不是強制獲取。

更新get方法是下面的人誰也有類似的需要:

@Transactional(readOnly=true) 
public <T,E> T get(ILazyGetter<T,E> p_getter) throws Exception { 
    // reattach the object to the session  
    E l_entity = getEntityManager().merge(p_getter.getEntity()); 

    T l_lazyData = p_getter.get(l_entity); 

    // Just getting the data doesn't necessarily initialize it - 
    // explicitly initialize it depending on the data type. 
    intializeEntity(l_lazyData); 

    // return the getter data 
    return l_lazyData; 
} 

/** 
* Attempt to initialize the entity so it is fully lazy loaded while in the transaction 
* @param p_entity 
*/ 
protected void intializeEntity(Object p_entity) { 
    if(p_entity != null && p_entity instanceof Collection) { 
    Collection<?> l_entity = (Collection<?>)p_entity; 
    l_entity.size(); // force the collection to load 
    } 
} 
+0

很高興你有它的工作,你會友好地標記爲一個選擇的答案:-) –