代码之家  ›  专栏  ›  技术社区  ›  artemb

更改保留其ID的实体的类型

  •  8
  • artemb  · 技术社区  · 15 年前

    我使用Hibernate作为持久层。在同一个表中有两个实体使用单表继承策略扩展一个超类。

    @Entity
    @Inheritance(strategy = InheritanceType.SINGLE_TABLE)
    public abstract class A {
        @Id
        @GeneratedValue
        protected Long id;
    
        // some common fields for B and C
    }
    
    
    @Entity
    public class B extends A {
        // B-specific fields
    }
    
    @Entity
    public class C extends A {
        // C-specific fields
    }
    

    我有一个b的实例,id=4。如何将此实例的类型更改为C并保留其ID(4)?

    B b = em.find(B.class, 4L);
    C c = convertToC(b);
    c.setId(b.getId());
    em.remove(b);
    em.persist(c);
    

    上面的代码失败

    org.hibernate.PersistentObjectException: detached entity passed to persist: C
    

    有可能吗?

    5 回复  |  直到 15 年前
        1
  •  11
  •   ChssPly76    15 年前

    Hibernate试图使持久性尽可能透明,这意味着它试图遵循与普通Java对象相同的原则。现在,用Java重述你的问题,你会得到:

    如何将B类的实例转换为(不兼容的)C类的实例?

    你知道答案-你不能。你可以创建一个 新的 C的实例并复制必要的属性,但B将 总是 是B,而不是C。因此,您最初的问题的答案是-它不能通过JPA或HibernateAPI完成。

    但是,与普通Java不同,用Hibernate可以欺骗: InheritanceType.SINGLE_TABLE 使用映射 @DiscriminatorColumn 为了将B转换成C,您需要将它的值从为B指定的值更新为为为C指定的值。诀窍是-您不能使用Hibernate API来完成它;您需要通过普通的SQL来完成它。但是,您可以将此更新语句映射为命名的SQL查询,并使用Hibernate工具执行它。

    因此,算法是:

    1. 驱逐 B如果有的话(这很重要)
    2. 执行命名查询。
    3. 使用前B的ID加载现在的C。
    4. 根据需要更新/设置属性。
    5. 坚持C
        2
  •  2
  •   skaffman    15 年前

    在这种情况下,“c”是一个Hibernate会话不知道的对象,但它有一个ID,因此它假定该对象已经被持久化。在该上下文中,persist()没有任何意义,因此失败了。

    javadoc for hibernate session.persiste()(我知道您没有使用hibernate API,但是语义相同,hibernate文档更好)说“使临时实例持久化”。如果您的对象已经有了一个ID,那么它不是暂时的。相反,它认为它是一个分离的实例(即已被持久化,但与当前会话没有关联的实例)。

    我建议您尝试merge()而不是persist()。

        3
  •  0
  •   David Rabinowitz    15 年前

    您可以使用自己的ID(未生成)并执行以下操作:

    1. 回复B
    2. 未结交易
    3. 删除B
    4. 提交事务
    5. 打开新事务
    6. 创建C并保持它
    7. 关闭第二个事务

    这样,在以C的形式重新插入之前,您将从表中清除ID。

        4
  •  0
  •   Damo    15 年前

    如何区分表中的两个实体?我假设有一些字段值(或值)可以改变,使B变成C?

    您可以使用一个方法来加载超级类A,更改区分值并保存。然后在下一个休眠会话中,您的b将是c。

        5
  •  0
  •   Petriborg    15 年前

    我认为斯卡夫曼就在这里,一旦设置了ID,它就不会持久,而且由于生成了ID,它期望序列负责分配ID号。

    您可能无法将ID设置为@generatedvalue?或者不同的生成器策略类型之一,可能是为了避免合并生成新的序列值,但我怀疑这会有问题。