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

Java 更改保留其ID的实体的类型,java,hibernate,persistence,Java,Hibernate,Persistence,我使用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

我使用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
}
我有一个id=4的B实例。如何将此实例的类型更改为C,以保留其ID(4)

上面的代码在以下情况下失败:

org.hibernate.PersistentObjectException: detached entity passed to persist: C
有可能吗?

在本例中,“c”是hibernate会话不知道的对象,但它有一个ID,因此它假设该对象已经被持久化。在这种情况下,persist()毫无意义,因此它失败了

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

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

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

  • 检索B
  • 公开交易
  • 删除B
  • 提交事务
  • 打开新交易
  • 创建C并将其持久化
  • 关闭第二笔交易

  • 这样,在将id作为C重新插入之前,您将从表中清除id。

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


    您可以有一个方法,在该方法中加载超类a并更改区分值并保存。然后,在下一次Hibernate会话中,您的B将是C。

    我认为skaffman就在这里,一旦设置了id,它将不会持久,而且由于生成了id,它希望序列负责分配id号


    您可能无法将id设置为@GeneratedValue?或者使用一种不同的生成器策略类型来避免合并生成新的序列值,但我认为这可能会有问题。

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

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

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

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

    因此,算法为:

  • 如果会话存在,则从会话中退出(这很重要)
  • 执行命名查询
  • 使用前B的id加载现在称为C的内容
  • 根据需要更新/设置属性
  • 持久性C

  • 我在问题中添加了一个异常只是一种预感,但是您是否尝试调用merge()而不是persist()?是的,merge为c提供了一个新的id。这可能是Entitymanager API不足的一个例子。它不像本机Hibernate API那样有表现力,有时意义不大。在本机Hibernate API中有这样做的方法吗?感谢您解释异常的原因,我会介意的。合并不会失败,但会给“c”一个新id,忽略已设置的内容。这将很难实现。我的所有实体都使用自动递增的ID。有没有办法强制生成id的值?Hibernate使用鉴别器列来确定加载数据时需要实例化哪个类。列的值等于类的简单名称。您是否建议使用普通SQL修改表数据?我看不出有别的办法可以按照你的建议去做。
    org.hibernate.PersistentObjectException: detached entity passed to persist: C