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
    列上使用了forced
    insertable=false,updateable=false
    (更改
    parent
    字段将生成正确的sql)