【发布时间】:2019-12-18 08:40:29
【问题描述】:
MxN 关系中有 3 个实体,B 为关联实体。我们在单个 TX 中创建它们,将它们全部持久化,并使用 OneToMany 关联获取实体。此关联在获取后未初始化。
来源:https://github.com/alfonz19/springboot222demo/commits/what
@Transactional
@Test
void contextLoads() {
// for(int i = 0; i < 3; i++) {
UUID aId = UUID.randomUUID();
AEntity aEntity = aRepository.save(new AEntity().setId(aId));
UUID bId = UUID.randomUUID();
CEntity cEntity = cRepository.save(new CEntity().setId(bId));
em.flush();
bRepository.save(new BEntity().setAEntity(aEntity).setCEntity(cEntity));
// }
em.flush();
// em.clear();
Iterable<CEntity> centities = cRepository.findAll();
List<BEntity> bEntities =
iterableToStream(centities).flatMap(e -> e.getBEntities().stream()).collect(Collectors.toList());
Assert.assertThat(centities, Matchers.iterableWithSize(1));
Assert.assertThat(bRepository.findAll(), Matchers.iterableWithSize(1));
Assert.assertThat(bEntities.size(), CoreMatchers.is(1));
...
}
好的,我明白了,在创建 BEntity 时,我不会更新 AEntity 和 CEntity 导致它们损坏。调用 cRepository.findAll() 然后确实调用 db 上的 select 以获取所有 C(即使没有任何 evict/flush/clear)但使 @OneToMany 未初始化。我不明白。我会理解,如果根本不调用 db,但如果我无论如何都获取 Cs 来刷新它,为什么不刷新关联表。这是为什么呢?
更令人惊讶的是 编辑:好的,这根本不奇怪,这就是级联合并的含义。完全没问题。aRepository.save(new AEntity().setId(aId)) 在执行 em.merge(实体已分配 id)时,即使 @OneToMany 是惰性的,休眠也会使用 2 个左外连接加载整个 MxN 结构。为什么会这样?
我对这种行为有点惊讶,因为在不应该出现的地方 (IIUC) 发出了选择,而在很容易出现的地方却没有。 p>
并将最好的保持到最后。稍作改动:取消注释 for 循环并清除,我得到了完全的非确定性行为。
来源:https://github.com/alfonz19/springboot222demo/tree/nondeterministic
测试要么工作,要么产生如下异常:
- 数组越界
- 拥有的实体实例不再引用具有 cascade="all-delete-orphan" 的集合:
- java.lang.NullPointerException
但是如果我在bEntities 变量声明上放置断点,cEntities 总是会正确创建并测试然后通过。我不知道是什么原因造成的。
【问题讨论】:
标签: jpa spring-data-jpa