如果在没有刷新的情况下发生添加和删除,orphanremoval 不会删除
orphanremoval doesn't delete if add and remove occur without flush
我只是想了解 Spring Jpa/Hibernate 的某些部分是如何工作的。正如标题所说,只有在从 collection 添加和删除 child 实体之间将实体刷新到数据库时,orphanRemoval 似乎才有效,我想知道为什么。
我有一个 parent class 与 @OneToMany 关联 child class
@Entity
class Parent {
@Id
@GeneratedValue
@Getter
private Long id;
@OneToMany(mappedBy = parent, cascade=ALL, orphanRemoval=true)
@Getter
private Set<Child> children;
public Parent(){
children = new HashSet<Child>();
}
public Child addChild(Child child){
child.setParent(this);
children.add(child);
return child;
}
public void removeChild(Child child){
child.setParent(null);
children.remove(child);
}
}
@Entity
class Child {
@Id
@GeneratedValue
@Getter
private Long id;
@ManyToOne
@Setter
private Parent parent;
}
我正在测试让 child 像这样从 parent 中删除(使用 @Autowired JPARepository 和 @JpaTest 注释)
@ExtendWith(SpringExtension.class)
@DataJpaTest
class PersistTest {
@Autowired ParentRepository repo; // JpaRepository<Parent, Long>
@Autowired EntityManager em;
@Test
public void whenChildRemoved_thenChildDeleted(){
Parent parent = new Parent();
Child child = parent.addChild(new Child());
repo.save(parent);
em.flush(); // test fails if removed
parent.removeChild(child);
repo.saveAndFlush(parent);
assertThat(repo.findById(parent.getId()).get().getChildren()).isEmpty();
assertThat(em.find(Child.class, child.getId()).isNull();
}
}
如果实体管理器在将 child 添加到 parent 和删除它之间没有刷新,那么两个断言都会失败,并且在查看生成的 sql 时没有 DELETE 语句制作。如果 EM 被刷新,则进行 DELETE 并且测试通过。
基本上只是想知道是否有人可以解释为什么会这样,以及将这两个操作放在单独的 @Transactional 方法中是否会产生相同的效果。
您期望完成或创建一些交易,但实际上并没有。这就是您看到此行为的原因。
@DataJpaTest
在每个方法上放置单独的事务(顺便说一句,默认情况下无论如何都会回滚);
- 这就是您可以使用
JpaRepository
的原因——它本身不创建交易(与 CrudRepository
相反),但有底层交易;
- 如果
JpaRepository
使用了@Transactional(REQUIRED_NEW)
,您可能会移除flush;
回答你最后一个问题。如果将这些操作放在单独的 @Transactional
方法中,它将以完全相同的方式工作,因为 @DataJpaTest
创建的测试中存在底层事务——hibernate 通常在最后刷新方法。你必须使用 @Transactional(REQUIRED_NEW)
.
我只是想了解 Spring Jpa/Hibernate 的某些部分是如何工作的。正如标题所说,只有在从 collection 添加和删除 child 实体之间将实体刷新到数据库时,orphanRemoval 似乎才有效,我想知道为什么。
我有一个 parent class 与 @OneToMany 关联 child class
@Entity
class Parent {
@Id
@GeneratedValue
@Getter
private Long id;
@OneToMany(mappedBy = parent, cascade=ALL, orphanRemoval=true)
@Getter
private Set<Child> children;
public Parent(){
children = new HashSet<Child>();
}
public Child addChild(Child child){
child.setParent(this);
children.add(child);
return child;
}
public void removeChild(Child child){
child.setParent(null);
children.remove(child);
}
}
@Entity
class Child {
@Id
@GeneratedValue
@Getter
private Long id;
@ManyToOne
@Setter
private Parent parent;
}
我正在测试让 child 像这样从 parent 中删除(使用 @Autowired JPARepository
@ExtendWith(SpringExtension.class)
@DataJpaTest
class PersistTest {
@Autowired ParentRepository repo; // JpaRepository<Parent, Long>
@Autowired EntityManager em;
@Test
public void whenChildRemoved_thenChildDeleted(){
Parent parent = new Parent();
Child child = parent.addChild(new Child());
repo.save(parent);
em.flush(); // test fails if removed
parent.removeChild(child);
repo.saveAndFlush(parent);
assertThat(repo.findById(parent.getId()).get().getChildren()).isEmpty();
assertThat(em.find(Child.class, child.getId()).isNull();
}
}
如果实体管理器在将 child 添加到 parent 和删除它之间没有刷新,那么两个断言都会失败,并且在查看生成的 sql 时没有 DELETE 语句制作。如果 EM 被刷新,则进行 DELETE 并且测试通过。
基本上只是想知道是否有人可以解释为什么会这样,以及将这两个操作放在单独的 @Transactional 方法中是否会产生相同的效果。
您期望完成或创建一些交易,但实际上并没有。这就是您看到此行为的原因。
@DataJpaTest
在每个方法上放置单独的事务(顺便说一句,默认情况下无论如何都会回滚);- 这就是您可以使用
JpaRepository
的原因——它本身不创建交易(与CrudRepository
相反),但有底层交易; - 如果
JpaRepository
使用了@Transactional(REQUIRED_NEW)
,您可能会移除flush;
回答你最后一个问题。如果将这些操作放在单独的 @Transactional
方法中,它将以完全相同的方式工作,因为 @DataJpaTest
创建的测试中存在底层事务——hibernate 通常在最后刷新方法。你必须使用 @Transactional(REQUIRED_NEW)
.