繁体   English   中英

JPA。如何将现有实体子类化并保留其ID?

[英]JPA. How do I subclass existing entity and keep its ID?

假设我有两个经典的非抽象JPA类:Person和Student。

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class Person {
  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  private String id;
  // ...
}

@Entity
public class Student extends Person {
  // ...
}

现在有一些身份证的人进入大学并成为一名学生。 如何在JPA中处理这个事实并保持个人身份?

student = new Student();
student.setPersonData(person.getPersonData());
student.setId(person.getId());
entityManager.persist(student);

上面的代码生成'传递给持久化的分离实体'异常,而使用entityManager.merge(student)跳过分配的id并创建具有新id的Person和Student的两个新实体。 任何想法如何保持原始ID?

JPA规范禁止应用程序更改实体的身份(第2.4节):

应用程序不得更改主键的值[10]。 如果发生这种情况,行为是不确定的。[11]

此外,对于使用连接的继承策略执行跨实体的继承的表,仅在根类中定义标识。 所有子类仅存储类的本地属性。

通过调用student.setId(person.getId()); 您试图将尚未持久化的实体的身份更改为现有实体的身份。 这本身没有意义,特别是因为您使用AUTO的序列生成策略(通常是TABLE)为身份生成值。

如果我们忽略了之前的观点,并且如果你希望将Person转换为Student,而不会丢失身份,那么这或多或少是不可能的(至少以干净的方式,正如@axtavt指出的那样)。 原因很简单,您无法在运行时成功地从Person向学生转发,因为这是您尝试在现实生活中执行的自然面向对象操作。 即使您以假设的方式成功进行了预测,原始实体也有一个需要修改的鉴别器列值; 在不知道JPA提供程序如何使用和缓存此值的情况下,使用本机SQL进行数据更改的任何尝试都可能导致比实际值更多的麻烦。

如果您不想丢失生成的ID(毕竟通常会生成它们,以便您可以使用自然键,或者您不必公开共享这些生成的ID),您应该创建Person对象的副本,将其重新创建为学生。 这将确保JPA提供程序也将正确填充鉴别器列。 此外,您还需要删除原始的Person实体。

以上所有,都在考虑您不会修改当前的对象模型。 如果您可以修改对象模型,则可能会保留原始ID。 这将要求您删除继承层次结构,因为它首先不适合您的域。 从一个人向学生倾斜的尝试表明继承不是一个自然的契合。 遵循@ axtavt的建议更合适,因为它实际上意味着有利于组合而不是继承,如果你仔细阅读(至少我是这样读的)。

JPA Wikibook在Object Reincarnation一节中讨论了这种情况。 请注意有关在实体中使用type属性而不是使用继承来更改对象类型的具体建议。

投胎

通常情况下,已移除的对象会被移除,但在某些情况下,您可能需要将对象恢复生命。 这通常发生在自然的id中,而不是生成的id,其中新的对象总是会获得新的id。 通常,重新生成对象的愿望来自不良对象模型设计,通常是改变对象的类类型的愿望(不能用Java完成,因此必须创建新对象)。 通常,最好的解决方案是更改对象模型,让对象保存一个定义其类型的类型对象,而不是使用继承。 但有时轮回是可取的。

据我所知,没有好办法实现它。

如果你对此建模一发而动这将是更simplier Person可以拥有多个角色Role S(一个一对多的关系),其中Student是其中之一(即Student伸出Role )。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM