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

Mic*_*vin 4 java collections orm hibernate 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
Run Code Online (Sandbox Code Playgroud)

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

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

    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);
Run Code Online (Sandbox Code Playgroud)

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

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

Aar*_*lla 8

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

  1. 关闭会话(在您的情况下可能不可能).
  2. 序列化对象并再次反序列化.
  3. 克隆对象并清除/ null主键/ id字段.

然后,您必须更改业务键(因此在使用未修改的实例false调用时将返回新equals()实例).这是重要的一步:没有它,Hibernate会将实例重新附加到数据库中的现有实例,否则您将得到其他奇怪的错误.

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


Pas*_*ent 6

Aaron Diguila的答案就是去这里的方式,即你需要detach你的实例,设置商业密钥null然后再设置persist它们.

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

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

Session session = (Session) em.getDelegate();
Run Code Online (Sandbox Code Playgroud)

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

然后,分离你的对象:

session.evict(object);
Run Code Online (Sandbox Code Playgroud)

更新:根据使用EntityManager.getDelegate()时要小心,使用 GlassFish应该实际使用(在您的情况下也可能):

org.hibernate.Session session = ((org.hibernate.ejb.EntityManagerImpl) em.getDelegate()).getSession();
Run Code Online (Sandbox Code Playgroud)

但这不适用于建议使用前面提到的代码的JBoss .

org.hibernate.Session session = (Session) em.getDelegate();
Run Code Online (Sandbox Code Playgroud)

虽然我理解使用getDelegate()JPA代码是不可移植的,但我必须承认我并不期望这个方法调用的结果是特定于实现的.

更新2:要回答问题的更新部分,我不确定您是否急切地加载了类别.这不是最好的方法,但如果你categories.get(0)在驱逐前打电话会怎么样?此外,我可能会错过那部分,但是,你在哪里取消了类别的关键?