复制一组实体并在Hibernate / JPA中保留

时间:2009-12-29 10:13:19

标签: java hibernate orm collections jpa

我想在我的数据库中复制一组实体。 我用以下方式收集了这个集合:

CategoryHistory chNew = new CategoryHistory();
CategoryHistory chLast =  (CategoryHistory)em.createQuery("SELECT ch from CategoryHistory ch WHERE ch.date = MAX(date)").getSingleResult;
List<Category> categories = chLast.getCategories();
chNew.addCategories(categories)// Should be a copy of the categories: OneToMany

现在我想复制一个'类别'列表并用EntityManager保存它。 我正在使用JPA / Hibernate。 的更新

在知道如何分离我的实体后,我需要知道要分离的内容: 当前代码:

    CategoryHistory chLast =  (CategoryHistory)em.createQuery("SELECT ch from CategoryHistory ch WHERE ch.date=(SELECT MAX(date) from CategoryHistory)").getSingleResult();
    Set<Category> categories =chLast.getCategories();

    //detach
    org.hibernate.Session session = ((org.hibernate.ejb.EntityManagerImpl) em.getDelegate()).getSession();
    session.evict(chLast);//detaches also its child-entities?       

    //set the realations
    chNew.setCategories(categories);
    for (Category category : categories) {
        category.setCategoryHistory(chNew);
    }
    //set now create date
    chNew.setDate(Calendar.getInstance().getTime());

    //persist
    em.persist(chNew);

这会引发failed to lazily initialize a collection of role: entities.CategoryHistory.categories, no session or session was closed例外。

我认为他想再懒得加载类别,因为我让它们分离了。我现在该怎么办?

4 个答案:

答案 0 :(得分:8)

您需要从会话中分离您的实例。有三种方法可以做到这一点:

  1. 关闭会话(在您的情况下可能不可能)。
  2. 序列化对象并再次反序列化。
  3. 克隆对象并清除/清空主键/ id字段。
  4. 然后您必须更改业务键(因此在使用未修改的实例调用false时,新实例将返回equals()。这是重要的一步:没有它,Hibernate会将实例重新附加到数据库中的现有实例,否则你会得到其他奇怪的错误。

    之后,您可以像保存其他任何实例一样保存新副本。

答案 1 :(得分:6)

Aaron Diguila的answer是这里的方式,即您需要detach您的实例,将商家密钥设置为null,然后persist

可悲的是,没有办法使用JPA 1.x将实体管理器中的一个对象断开连接(JPA 2.0将EntityManager.detach(Object)并修复此问题)。所以,要么等待JPA 2.x(我猜不是一个选项),要么使用Hibernate的底层Session

为此,您可以将EntityManager的委托转换为Hibernate会话。

Session session = (Session) em.getDelegate();

当然,这仅在您将Hibernate用作Java持久性提供程序时才有效,因为委托是Session API。

然后,分离你的对象:

session.evict(object);

更新:根据Be careful while using EntityManager.getDelegate(),使用GlassFish实际上应该使用(也可能在您的情况下):

org.hibernate.Session session = ((org.hibernate.ejb.EntityManagerImpl) em.getDelegate()).getSession();

但是这将在JBoss中suggest使用前面提到的代码。

org.hibernate.Session session = (Session) em.getDelegate();

虽然我知道使用getDelegate()会使JPA代码不可移植,但我必须承认我并不期望此方法调用的结果是特定于实现的。

UPDATE2:要回答问题的更新部分,我不确定您是否急切地加载了这些类别。这不是执行此操作的最佳方式,但如果您在驱逐前致电categories.get(0)会发生什么?另外,我可能会错过那部分,但是,你在哪里取消了类别的关键?

答案 2 :(得分:0)

确定,

由于我使用的是glassfish v3,而JPA2.0是最终的,我使用了EntityManager.detach() 奇怪的是ejb3-persistence.jar包含在我的lib中,所以我把它抛出并使用了glassfish jar的javax.persistence。分离方法在那里,但我的hibernate版本还没有实现

答案 3 :(得分:0)

  1. 克隆或复制每个对象的属性。您可以使用Apache BeanUtils.copyProperties(copy, orig)
  2. 在OpenJPA中,使用Apache BeanUtils手动删除实体监控:

    BeanUtils.setProperty(copy, "pcVersionInit", false);

  3. 将主键设置为default / null。

  4. 坚持每份。
  5. 另见:http://www.java-tutorial.ch/java-persistence-api/how-to-persist-duplicate-of-an-entity-with-openjpa