Hibernate 如何使用共享主键保存子实体 问题
我有两个实体,Hibernate 如何使用共享主键保存子实体 问题,hibernate,spring-data-jpa,one-to-one,Hibernate,Spring Data Jpa,One To One,我有两个实体,Parent和Child,我希望它们共享主键,但我希望关系是单向的。只有孩子才应该知道父母的情况。这在spring数据jpa存储库中可能吗 我试过的 我尝试过直接使用EntityManager,它很有效。我能够首先持久化父实体,然后持久化子实体。但是,当我尝试使用Spring的JpaRepository执行相同操作时,我得到以下错误:org.springframework.orm.jpa.JpaSystemException:尝试从空的一对一属性分配id 我尝试过用它自己的序列i
Parent
和Child
,我希望它们共享主键,但我希望关系是单向的。只有孩子才应该知道父母的情况。这在spring数据jpa存储库中可能吗
我试过的
- 我尝试过直接使用EntityManager,它很有效。我能够首先持久化父实体
,然后持久化子实体
。但是,当我尝试使用Spring的
执行相同操作时,我得到以下错误:JpaRepository
org.springframework.orm.jpa.JpaSystemException:尝试从空的一对一属性分配id
- 我尝试过用它自己的序列id和对父实体的外键引用对
子实体进行不同的建模。这是可行的,但我更喜欢使用共享主键
@实体
公共类父类{
@身份证
@列(name=“code”)
字符串代码;
}
子实体:
@实体
公营儿童{
@身份证
@列(name=“code”)
字符串代码;
@MapsId
@JoinColumn(name=“code”)
@奥内托内
父母;
}
测试:
@RunWith(SpringRunner.class)
@DataJpaTest
@Slf4j
公共类MapsBydTest{
@自动连线
childRepo;
@自动连线
ParentRepo ParentRepo;
@自动连线
实体管理器实体管理器;
@测试//因org.springframework.orm.jpa.JpaSystemException失败
public void testSpringDataJpaRepository(){
父pA=parentRepo.save(Parent.builder().code(“A”).build());
Child=Child.builder().parent(pA.code(“A”).build();
儿童救助(儿童);
}
@测试//工作!
public void testEntityManager(){
父p=Parent.builder().code(“A”).build();
实体管理器(p);
Child=Child.builder().code(“A”).parent(p.build();
entityManager.persist(child);
log.info(entityManager.find(Parent.class,“A”).toString());
log.info(entityManager.find(Child.class,“A”).toString());
}
}
以下代码可以正常工作:
public class Child {
@Id
@Column(name = "code", insertable = false, updatable = false)
String code;
@MapsId
@JoinColumn(name = "code")
@OneToOne
Parent parent;
}
和测试
@Test
@Transactional
public void testSpringDataJpaRepository() {
Parent pA = parentRepo.save(Parent.builder().code("A").build());
Child child = Child.builder().parent(pA).build();
childRepo.save(child);
}
解释:
查看SimpleParepository.save的实现
@Transactional
public <S extends T> S save(S entity) {
if (entityInformation.isNew(entity)) {
em.persist(entity);
return entity;
} else {
return em.merge(entity);
}
}
@Transactional
公共存储(S实体){
if(entityInformation.isNew(实体)){
em.persist(实体);
返回实体;
}否则{
返回em.merge(实体);
}
}
然后选中AbstractEntityInformation.isNew
。
它得出结论,只有当实体的值为null(或数字类型为0)时,实体才是新的。
因此,您的childRepo.save(child)
相当于entityManager.merge(子级)代码>
如果在第二次测试中调用merge,请检查收到的错误是否相同
要解决这个问题:
- 不要在您的子
@Id
上设置值(可能您也希望强制lombok不为其生成setter)。这将导致调用persist
,而不是merge
- 请注意,有两个字段
code
和parent
映射到同一db列。为了使映射正确,我在@Id
列上使用了forcedinsertable=false,updateable=false
(更改parent
字段将生成正确的sql)