我对JPA 2.0 orphanRemoval
属性有些困惑。
我想我可以看到,当我使用JPA提供程序的数据库生成工具来创建基础数据库DDL时,需要具有ON DELETE CASCADE
特定的关系。
但是,如果数据库存在并且已经具有ON DELETE CASCADE
on关系,这是否不足以适当地级联删除?orphanRemoval
另外还做什么?
干杯
我对JPA 2.0 orphanRemoval
属性有些困惑。
我想我可以看到,当我使用JPA提供程序的数据库生成工具来创建基础数据库DDL时,需要具有ON DELETE CASCADE
特定的关系。
但是,如果数据库存在并且已经具有ON DELETE CASCADE
on关系,这是否不足以适当地级联删除?orphanRemoval
另外还做什么?
干杯
Answers:
orphanRemoval
与无关ON DELETE CASCADE
。
orphanRemoval
是一个完全ORM特定的东西。当不再从“父”实体中引用“子”实体时,例如,当您从父实体的相应集合中删除子实体时,它会将“子”实体标记为要删除。
ON DELETE CASCADE
是特定于数据库的事物,当删除“父”行时,它将删除数据库中的“子”行。
此处采用的示例:
当一个Employee
实体对象被移除,删除操作被级联到引用的Address
实体对象。在这方面,orphanRemoval=true
和cascade=CascadeType.REMOVE
相同,如果orphanRemoval=true
指定,CascadeType.REMOVE
则是多余的。
两种设置之间的区别在于对断开关系的响应。例如,例如在将地址字段设置null
为另一个Address
对象时。
如果orphanRemoval=true
指定为,则断开连接的Address
实例将自动删除。这对于清理Address
没有所有者对象(例如Employee
)的引用不应该存在的依赖对象(例如)很有用。
如果仅cascade=CascadeType.REMOVE
指定,则不执行任何自动操作,因为断开关系不是删除操作。
为避免由于孤立删除而导致引用悬而未决,应仅对包含私有非共享依赖对象的字段启用此功能。
我希望这一点更加清楚。
从集合中删除子实体之后,您还将同时从数据库中删除该子实体。orphanRemoval还意味着您无法更改父母;如果有一个部门有员工,那么一旦您将该员工删除以放入另一个部门,您将无意中在刷新/提交时将该员工从数据库中删除(以先到者为准)。士气是将orphanRemoval设置为true,只要您确定该父级的子级在整个存在期间都不会迁移到另一个父级即可。启用orphanRemoval也会自动将REMOVE添加到级联列表。
department.remove(emp);
该员工,便会从emp表中删除该员工,甚至都不会打电话commit()
用于DDL等效JPA映射ON DELETE CASCADE
是cascade=CascadeType.REMOVE
。孤立删除意味着从属实体在与“父”实体的关系被破坏时也被删除。例如,如果从@OneToMany
关系中删除了一个孩子却没有在实体管理器中显式删除它。
cascade=CascadeType.REMOVE
不等于ON DELETE CASCADE
。启用时,请删除应用程序代码,并且不会影响DDL,其他会在DB中执行。参见stackoverflow.com/a/19696859/548473
JPA将实体状态转换转换为SQL语句,例如INSERT,UPDATE或DELETE。
当您persist
是实体时,您计划将INSERT语句安排在EntityManager
刷新时自动或手动执行。
当您remove
是实体时,您正在计划DELETE语句,该语句将在刷新持久性上下文时执行。
为了方便起见,JPA允许您传播从父实体到子实体的实体状态转换。
因此,如果您有一个与Post
子实体有@OneToMany
关联的父PostComment
实体:
实体中的comments
集合Post
映射如下:
@OneToMany(
mappedBy = "post",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<Comment> comments = new ArrayList<>();
该cascade
属性告诉JPA提供者将实体状态从父Post
实体传递到集合中PostComment
包含的所有实体comments
。
因此,如果您删除Post
实体:
Post post = entityManager.find(Post.class, 1L);
assertEquals(2, post.getComments().size());
entityManager.remove(post);
JPA提供程序将首先删除该PostComment
实体,并且在删除所有子实体时,它也会同时删除该Post
实体:
DELETE FROM post_comment WHERE id = 1
DELETE FROM post_comment WHERE id = 2
DELETE FROM post WHERE id = 1
当您将orphanRemoval
属性设置true
为时,remove
当从集合中删除子实体时,JPA提供程序将安排操作。
因此,就我们而言
Post post = entityManager.find(Post.class, 1L);
assertEquals(2, post.getComments().size());
PostComment postComment = post.getComments().get(0);
assertEquals(1L, postComment.getId());
post.getComments().remove(postComment);
JPA提供者将删除关联的post_comment
记录,因为PostComment
在comments
集合中不再引用该实体:
DELETE FROM post_comment WHERE id = 1
的ON DELETE CASCADE
是在FK级别定义:
ALTER TABLE post_comment
ADD CONSTRAINT fk_post_comment_post_id
FOREIGN KEY (post_id) REFERENCES post
ON DELETE CASCADE;
完成后,如果您删除post
一行:
DELETE FROM post WHERE id = 1
post_comment
数据库引擎会自动删除所有关联的实体。但是,如果您误删除了根实体,这将是非常危险的操作。
JPA cascade
和orphanRemoval
选件的优点在于,您还可以从乐观锁定中受益,以防止更新丢失。
如果您使用JPA级联机制,则无需使用DDL级ON DELETE CASCADE
,如果删除具有多个子级实体的根实体,这将是非常危险的操作。
CascadeType
。这是一种补充机制。现在,您误删除了持久性。孤儿删除是关于删除未引用的关联,而持久化是关于保存新实体。您需要按照答案中提供的链接来更好地理解这些概念。
@GaryK的回答绝对很棒,我花了一个小时来寻找orphanRemoval = true
vs 的解释CascadeType.REMOVE
,它有助于我理解。
总结:仅当我们删除对象()并且希望也删除子对象时,其orphanRemoval = true
工作原理相同。CascadeType.REMOVE
entityManager.delete(object)
在完全不同的情况下,当我们使用获取类似的数据List<Child> childs = object.getChilds()
然后删除child(entityManager.remove(childs.get(0)
)时,orphanRemoval=true
将导致childs.get(0)
与之相对应的实体将从数据库中删除。
在以下情况下,孤儿删除的效果与“ ON DELETE CASCADE”相同:-假设我们在学生实体和指导实体之间存在简单的多对一关系,其中许多学生可以映射到同一指南,并且在数据库中有一个学生表和向导表之间的外键关系,以使学生表的id_guide为FK。
@Entity
@Table(name = "student", catalog = "helloworld")
public class Student implements java.io.Serializable {
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "id")
private Integer id;
@ManyToOne(cascade={CascadeType.PERSIST,CascadeType.REMOVE})
@JoinColumn(name = "id_guide")
private Guide guide;
//父实体
@Entity
@Table(name = "guide", catalog = "helloworld")
public class Guide implements java.io.Serializable {
/**
*
*/
private static final long serialVersionUID = 9017118664546491038L;
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "id", unique = true, nullable = false)
private Integer id;
@Column(name = "name", length = 45)
private String name;
@Column(name = "salary", length = 45)
private String salary;
@OneToMany(mappedBy = "guide", orphanRemoval=true)
private Set<Student> students = new HashSet<Student>(0);
在这种情况下,这种关系使得学生实体是关系的所有者,因此我们需要保存该学生实体以持久保存整个对象图,例如
Guide guide = new Guide("John", "$1500");
Student s1 = new Student(guide, "Roy","ECE");
Student s2 = new Student(guide, "Nick", "ECE");
em.persist(s1);
em.persist(s2);
在这里,我们将同一指南与两个不同的学生对象映射,并且由于使用了CASCADE.PERSIST,因此对象图将按以下方式保存在数据库表中(在我的情况下为MySql)
学生表:-
1罗伊ECE 1
2尼克ECE 1
1约翰$ 1500
现在,如果我想删除其中一名学生,请使用
Student student1 = em.find(Student.class,1);
em.remove(student1);
当删除学生记录时,也应删除相应的指南记录,这就是Student实体中CASCADE.REMOVE属性出现的位置,它的作用是;它将删除标识符为1的学生以及相应的指南对象(标识符1)。但是在此示例中,还有一个学生对象被映射到同一指南记录,并且除非我们在指南实体中使用orphanRemoval = true属性,否则上面的删除代码将不起作用。