通过合并了解关系级联
Understanding Relationship Cascading with Merge
深入研究 Spring JPA,我很难理解这里发生了什么。
这是代码:
实体
@Entity
public class PersonP {
@Id @GeneratedValue
private int id;
public String name;
@ManyToMany
public List<PersonP> knows = new LinkedList<>();
@ManyToMany(mappedBy = "knows", cascade = CascadeType.ALL)
public List<PersonP> knownBy = new LinkedList<>();
public PersonP(){}
public PersonP(String name) {
this.name = name;
}
}
SpringBootApp
@SpringBootApplication
@EntityScan(basePackageClasses = Demo.class)
public class Demo implements CommandLineRunner {
@PersistenceContext
EntityManager em;
public static void main(String[] args) {
new SpringApplicationBuilder(Demo.class).run(args);
}
@Override
@Transactional
public void run(String... args) throws Exception {
sample();
}
@Transactional
public void sample(){
PersonP p1 = new PersonP("P1");
PersonP p2 = new PersonP("P2");
PersonP p3 = new PersonP("P3");
PersonP p4 = new PersonP("P4");
PersonP p5 = new PersonP("P5");
p1.knows.add(p2);
p2.knows.add(p3);
p5.knows.add(p3);
p1.knownBy.add(p3);
p3.knownBy.add(p2);
p2.knownBy.add(p4);
p1 = em.merge(p1);
p1.name = "x1";
p2.name = "x2";
p3.name = "x3";
p4.name = "x4";
p5.name = "x5";
}
通过启动应用程序,数据库中会生成以下内容:
我已经到了生成所有人员的地步,但我希望 PERSONP_KNOWS
table 中有一个额外的条目,例如p2.knownBy.add(p4);
但事实并非如此。我假设自从调用 merge
之后,关联的实体也会被保存(p1 知道 p2,p4 知道 p2)。不过好像不是这样的……
为什么会这样?
如您所见,并不是所有的人都被持久化(P5
在数据库中缺失),而且您可能想要的所有关系也不是。
缺少 P5
的原因是您仅沿着 knownBy
相关人员级联合并操作:
P1
-> P3
-> P2
-> P4
(P5
不是 knownBy
任何人,因此级联合并未达到)
当四个实体中的每一个都被持久化时,关于它拥有拥有方的关联的信息也会被持久化。
在你的例子中,拥有方是knows
(因为knownBy
是mappedBy="knows")。
所以唯一保留的 association-information 是:
P1
(ID1) -> P2
(ID3)
P2
(ID3) -> P3
(ID2)
(请注意,P2
具有 ID 3,这令人困惑,但由于 ID 已生成且后来被保留,因此有意义)
P5
-> P3
不会持久化,因为 P5
根本不会持久化(如上所述)
因此,要正确保存所有信息,请始终确保 bi-directional 关联同步。并注意在 @ManyToMany
关联上进行级联操作时的潜在问题,因为这可能导致性能问题(大量级联操作)和意外结果(例如,当级联删除时)。
https://vladmihalcea.com/jpa-hibernate-synchronize-bidirectional-entity-associations/
https://thorben-janssen.com/avoid-cascadetype-delete-many-assocations/
深入研究 Spring JPA,我很难理解这里发生了什么。 这是代码:
实体
@Entity
public class PersonP {
@Id @GeneratedValue
private int id;
public String name;
@ManyToMany
public List<PersonP> knows = new LinkedList<>();
@ManyToMany(mappedBy = "knows", cascade = CascadeType.ALL)
public List<PersonP> knownBy = new LinkedList<>();
public PersonP(){}
public PersonP(String name) {
this.name = name;
}
}
SpringBootApp
@SpringBootApplication
@EntityScan(basePackageClasses = Demo.class)
public class Demo implements CommandLineRunner {
@PersistenceContext
EntityManager em;
public static void main(String[] args) {
new SpringApplicationBuilder(Demo.class).run(args);
}
@Override
@Transactional
public void run(String... args) throws Exception {
sample();
}
@Transactional
public void sample(){
PersonP p1 = new PersonP("P1");
PersonP p2 = new PersonP("P2");
PersonP p3 = new PersonP("P3");
PersonP p4 = new PersonP("P4");
PersonP p5 = new PersonP("P5");
p1.knows.add(p2);
p2.knows.add(p3);
p5.knows.add(p3);
p1.knownBy.add(p3);
p3.knownBy.add(p2);
p2.knownBy.add(p4);
p1 = em.merge(p1);
p1.name = "x1";
p2.name = "x2";
p3.name = "x3";
p4.name = "x4";
p5.name = "x5";
}
通过启动应用程序,数据库中会生成以下内容:
我已经到了生成所有人员的地步,但我希望 PERSONP_KNOWS
table 中有一个额外的条目,例如p2.knownBy.add(p4);
但事实并非如此。我假设自从调用 merge
之后,关联的实体也会被保存(p1 知道 p2,p4 知道 p2)。不过好像不是这样的……
为什么会这样?
如您所见,并不是所有的人都被持久化(P5
在数据库中缺失),而且您可能想要的所有关系也不是。
缺少 P5
的原因是您仅沿着 knownBy
相关人员级联合并操作:
P1
-> P3
-> P2
-> P4
(P5
不是 knownBy
任何人,因此级联合并未达到)
当四个实体中的每一个都被持久化时,关于它拥有拥有方的关联的信息也会被持久化。
在你的例子中,拥有方是knows
(因为knownBy
是mappedBy="knows")。
所以唯一保留的 association-information 是:
P1
(ID1) -> P2
(ID3)
P2
(ID3) -> P3
(ID2)
(请注意,P2
具有 ID 3,这令人困惑,但由于 ID 已生成且后来被保留,因此有意义)
P5
-> P3
不会持久化,因为 P5
根本不会持久化(如上所述)
因此,要正确保存所有信息,请始终确保 bi-directional 关联同步。并注意在 @ManyToMany
关联上进行级联操作时的潜在问题,因为这可能导致性能问题(大量级联操作)和意外结果(例如,当级联删除时)。
https://vladmihalcea.com/jpa-hibernate-synchronize-bidirectional-entity-associations/
https://thorben-janssen.com/avoid-cascadetype-delete-many-assocations/