假设我有两个经典的非抽象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的两个新实体。任何想法我如何保留原始身份证?
答案 0 :(得分:5)
JPA规范禁止应用程序更改实体的身份(第2.4节):
应用程序不得更改主键的值[10]。如果发生这种情况,行为是不确定的。[11]
此外,对于使用连接的继承策略执行跨实体的继承的表,仅在根类中定义标识。所有子类只存储该类的本地属性。
通过调用student.setId(person.getId());
,您试图将尚未持久化的实体的身份更改为现有实体的身份。这本身没有意义,特别是因为您使用AUTO的序列生成策略(通常是TABLE)为身份生成值。
如果我们忽略了之前的观点,并且如果你希望将人员转换为学生,而不会失去身份,那或多或少是不可能的(至少以干净的方式,正如@axtavt指出的那样) )。原因很简单,您无法在运行时成功地从Person向学生转发,因为这是您尝试在现实生活中执行的自然面向对象操作。即使您以假设的方式成功进行了预测,原始实体也有一个需要修改的鉴别器列值;在不知道JPA提供程序如何使用和缓存此值的情况下,使用本机SQL进行数据更改的任何尝试都可能导致比实际值更多的麻烦。
如果您不想丢失生成的ID(毕竟通常会生成它们,以便您可以使用自然键,或者您不必公开共享这些生成的ID),您应该创建一个Person对象并将其重新创建为Student。这将确保JPA提供程序也将正确填充鉴别器列。此外,您还需要删除原始的Person实体。
以上所有,正在考虑您不会修改当前的对象模型。如果您可以修改对象模型,则可能会保留原始ID。这将要求您删除继承层次结构,因为它首先不适合您的域。从一个人向学生倾斜的尝试表明继承不是一个自然的契合。遵循@ axtavt的建议更合适,因为它实际上意味着有利于构图而不是继承,如果你仔细阅读(至少我是这样读的)。
JPA Wikibook在 Object Reincarnation 一节中讨论了这种情况。请注意有关在实体中使用type
属性而不是使用继承来更改对象类型的具体建议。
<强>轮回强>
通常是一个已经存在的对象 删除,保持删除,但在一些 您可能需要携带物品的情况 起死回生。这通常发生 天生的ids,不是生成的, 一个新对象永远得到的地方 新的身份证。一般的愿望 重生一个对象发生在一个 坏对象模型设计,通常是 希望改变一个类的类型 对象(不能用Java完成, 所以必须创建一个新对象)。 通常最好的解决方案是 改变你的对象模型让你的 对象持有一个类型对象 定义其类型,而不是使用 遗产。但有时候 轮回是可取的。
答案 1 :(得分:4)
据我所知,没有好办法实现它。
如果您将此情况建模为可以具有多个角色Person
s(一对多关系)的Role
,则会更加简单,其中Student
就是其中之一(即Student
延伸Role
)。