gpt4 book ai didi

hibernate - 如何使用共享主键保存子实体

转载 作者:行者123 更新时间:2023-12-05 04:00:52 25 4
gpt4 key购买 nike

问题

我有两个实体,ParentChild,我希望它们共享一个主键,但我希望这种关系是单向的。只有 child 应该知道 parent 的事。 spring-data-jpa 存储库可以做到这一点吗?

我尝试过的

  • 我试过直接使用 EntityManager 并且它有效。我可以先保留 Parent,然后保留 Child 实体。但是,当我尝试使用 Spring 的 JpaRepository 进行相同操作时,出现以下错误:org.springframework.orm.jpa.JpaSystemException: attempted to assign id from null one-to-one property.

  • 我已经尝试使用它自己的 SERIAL id 和对父项的外键引用以不同方式对 Child 实体建模。这可行,但我更愿意使用共享主键。

父实体:

@Entity
public class Parent {

@Id
@Column(name = "code")
String code;

}

子实体:

@Entity
public class Child {

@Id
@Column(name = "code")
String code;

@MapsId
@JoinColumn(name = "code")
@OneToOne
Parent parent;
}

测试:

@RunWith(SpringRunner.class)
@DataJpaTest
@Slf4j
public class MapsByIdTest {

@Autowired
ChildRepository childRepo;

@Autowired
ParentRepo parentRepo;

@Autowired
EntityManager entityManager;

@Test // FAILS with org.springframework.orm.jpa.JpaSystemException
public void testSpringDataJpaRepository() {

Parent pA = parentRepo.save(Parent.builder().code("A").build());

Child child = Child.builder().parent(pA).code("A").build();

childRepo.save(child);
}

@Test // WORKS!
public void testEntityManager() {

Parent p = Parent.builder().code("A").build();
entityManager.persist(p);

Child 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);
}

解释:看SimpleJpaRepository.save的实现

@Transactional
public <S extends T> S save(S entity) {

if (entityInformation.isNew(entity)) {
em.persist(entity);
return entity;
} else {
return em.merge(entity);
}
}

然后检查 AbstractEntityInformation.isNew。它得出结论,只有当实体为空(或数字类型为 0)时,该实体才是新的。因此,您的 childRepo.save(child); 等同于 entityManager.merge(child);检查如果您在第二次测试中调用合并,您收到的错误是否相同。

解决问题:

  • 不要为你的 child @Id 设置值(可能你想强制 lombok 也不要为它生成 setter)。这将导致 persist 被 alled 而不是 merge
  • 请注意,您有 2 个字段 codeparent 映射到同一个数据库列。为了使映射正确,我在 @Id 列上使用了强制 insertable = false, updatable = false(parent 字段的更改将导致要生成的正​​确的 sql)

关于hibernate - 如何使用共享主键保存子实体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55874648/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com