JPA: Spring Data JPA @OneToMany 注解参数 orphanRemoval,一对多删除详解
分析了OneToMany级联操作多方的插入、更新、删除。我们得到如下结论:
1、插入,建议一方设置mappedBy,好处是只会执行一条insert语句。不会执行多余的update外键的sql。
2、更新,没有区别
3、删除,一方设置mappedBy。一方维护的多方集合remove,多方显示删除。
orphanRemoval
插入和更新都没有什么问题。但是删除就有些奇怪了,一方和多方均要操作,如果看过前面文章分析,倒也是合情合理。但操作起来实在是麻烦,今天codereview时,研发小伙伴们也提出疑问。如果一方通过对多方集合的remove操作即触发删除(无需多方显式删除),那就方便多了,而且直观好理解。可惜通过前文的实验,发现设置了mappedBy,单纯的在集合中remove不会有任何效果;不设置mappedBy,集合中remove只会把多方的外键update为null。并不能达到删除的目的。
难道真的不行?我又打开OneToMany的代码,发现这么一个属性:
/*** (Optional) Whether to apply the remove operation to entities that have* been removed from the relationship and to cascade the remove operation to* those entities.* @since Java Persistence 2.0*/boolean orphanRemoval() default false;
看注释,说的很明白,如果设置为true,当关系被断开时,多方实体将被删除。这不正是我们想要的效果嘛?!这么重要的参数之前怎么没注意到呢?回想一下,其实之前我也是试过这个参数的,但是印象中并没有达到我想要的效果,应该是没有执行删除的操作。但是这黑纸白字写的清清楚楚的注释,不应该啊。。。。
我决定再测试一次,于是修改了我的配置,加上 orphanRemoval=true :
public class User {@Id@GeneratedValue(strategy = GenerationType.AUTO)private Long id;private String userName;private String password;@Fetch(FetchMode.SUBSELECT)@OneToMany(cascade = {CascadeType.ALL}, fetch = FetchType.LAZY, orphanRemoval=true, mappedBy = "user")private List<ContactInfo> contactInfos = new ArrayList<>();
}
持久化代码:
User user=userRepository.findById(1L).get();
ContactInfo deletedContact = user.getContactInfos().get(0);
user.getContactInfos().remove(deletedContact);
userRepository.save(user);
持久化代码只在一方维护的list中remove掉想删除的数据
运行程序,看到如下sql输出:
Hibernate: delete from contact_info where id=?
成功了!!
这是怎么回事呢?我清楚记得之前测试过这个设置,但没能删除成功。
犯的错误
难道是之前没有设置mappedBy吗?于是我去掉mappedBy再次测试下,配置如下:
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String userName;
private String password;
@Fetch(FetchMode.SUBSELECT)
@OneToMany(cascade = {CascadeType.ALL}, fetch = FetchType.LAZY, orphanRemoval=true)
@JoinColumn(name = "user_id")
private List<ContactInfo> contactInfos = new ArrayList<>();
}
运行下,打印如下sql:
Hibernate: update contact_info set user_id=null where user_id=? and id=?
然后报错了:
com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Column 'user_id' cannot be null
这是因为没有设置mappedBy,所以在liste中remove的操作,一方要维护关系,体现在update多方外键为空。但因为数据库设置了not null的约束,所以报错。
真相大白
其实到现在真相已经水落石出了。由于之前我使用orphanRemoval时,没有设置mappedBy,所以先执行了update语句(一方要维护关系)。但因为没有删除动作发生,并且程序报错,我错误的认为orphanRemoval=true没有效果。JPA代码注释里也没有提到和mappedBy的关系,也是导致当时我判断错误的原因之一。
不过注释没提到这一点,现在我也是理解的,因为如果外键没有设置not null,在update之后是会执行delete的(已经测试过)。orphanRemoval和mappedBy本来就是两个相互独立的属性,每人负责自己的事情,但是搭配不当就会产生意料之外的效果,并且很容易让人产生误会。JPA坑就坑在这里,不过搞懂每个属性作用,以及执行的顺序后,我们运用起来就自如多了。
到这里还没有完,我提个问题:
设置了orphanRemoval=true,导致级联删除。这和oneToMany配置的cascade有关系吗?
答案是没有关系,我们设置cascade = {CascadeType.PERSIST,CascadeType.MERGE},不设置REMOVE操作,发现一样可以通过从集合中remove来删除多方。这里再次印证了上篇文章开头我说的:CascadeType.REMOVE只是指删除一方,是否把关联多方全部删除。
总结
我们再聊下mappedBy和orphanRemoval,在一方维护的list中remove掉多方时产生的效果。
之前我犯的错误就是未设置mappedBy,但设置了orphanRemoval。但是JPA会先执行mappedBy未设置产生的update语句,导致not null报错。而我错误的认为这是orphanRemoval引起的(或者说没产生删除效果)。
本篇文章到这里就结束了,我们得出一个结论,如果想通过对一方维护的多方集合做remove操作,就达到删除多方数据的效果,那么需要同时设置 orphanRemoval=true, mappedBy = "一方对象"。如果不设置orphanRemoval=true,那么需要额外显式删除多方对象。
结束语
其实所有看到的都是表象,我们记住那么多实验结果不可能也没有必要。我们只需要记住每个属性的设置分别产生什么效果就好。如果这个都记不住,那就记最重要的orphanRemoval=true, mappedBy = "一方对象"。不过这个真的是万能的吗?
悬念
如果按照前文的配置方式,在一方维护的list上既add也remove,理论上会既插入又删除。我做了测试也证明确实如此。但是会有什么潜在问题吗?假如数据库设置了某个字段的唯一性约束,remove掉的数据和insert的数据该属性值相同(全删全导方式会出现此场景),这种方式会有什么问题吗?大家可以做下实验,我下篇文章再继续写。本文就到此为止了~
JPA: Spring Data JPA @OneToMany 注解参数 orphanRemoval,一对多删除详解相关推荐
- JPA Spring Data JPA详解
JPA & Spring Data JPA 一.JPA 1. JPA是什么 JPA(Java Persistence API)Java持久化 API,是一套基于ORM思想的规范. ORM(Ob ...
- Spring Data JPA OneToMany注解参数orphanRemoval,一对多删除详解
博主:爱码叔 个人博客站点: icodebook 公众号:漫话软件设计 专注于软件设计与架构.技术管理.擅长用通俗易懂的语言讲解技术.对技术管理工作有自己的一定见解.文章会第一时间首发在个站上,欢迎大 ...
- hql实例 jpa_SpringBoot学习笔记九:Spring Data Jpa的使用
Spring Data Jpa 简介 JPA JPA(Java Persistence API)意即Java持久化API,是Sun官方在JDK5.0后提出的Java持久化规范(JSR 338,这些接口 ...
- 【SpringBoot框架篇】11.Spring Data Jpa实战
文章目录 1.简介 1.1.JPA 1.2.Spring Data Jpa 1.3.Hibernate 1.4.Jpa.Spring Data Jpa.Hibernate三者之间的关系 2.引入依赖 ...
- Spring Boot(五):spring data jpa的使用
Spring Boot(五):spring data jpa的使用 一.spring data jpa介绍 1,JPA是什么 JPA(Java Persistence API)是Sun官方提出的Jav ...
- spring-boot (三) spring data jpa
学习文章来自:http://www.ityouknow.com/spring-boot.html spring data jpa介绍 首先了解JPA是什么? JPA(Java Persistence ...
- 干货|一文读懂 Spring Data Jpa!
有很多读者留言希望松哥能好好聊聊 Spring Data Jpa!其实这个话题松哥以前零零散散的介绍过,在我的书里也有介绍过,但是在公众号中还没和大伙聊过,因此本文就和大家来仔细聊聊 Spring D ...
- Spring Data JPA例子[基于Spring Boot、Mysql]
关于Spring Data Spring社区的一个顶级工程,主要用于简化数据(关系型&非关系型)访问,如果我们使用Spring Data来开发程序的话,那么可以省去很多低级别的数据访问操作,如 ...
- JPA、Hibernate和Spring Data JPA区别
大家好,我是神韵,是一个技术&生活博主.出文章目的主要是两个,一是好记忆不如烂笔头,记录总结中提高自己.二是希望我的文章可以帮到大家.欢迎来点赞打卡,你们的行动将是我无限的动力. 本篇主题是: ...
最新文章
- 切换Python2和Python3的方法
- 【fjwc2015】世界树
- android 程序退出广播,android 利用广播实现程序的强制退出
- linux测试地址是否能访问_一个小测试能看出孩子注意力是否集中,提前弥补,上小学会很轻松...
- Oracle DBA必须学会的11个Linux基本命令
- 关于徒手脱壳的几种方法
- 20200501:力扣185周赛上
- SQL JOIN--初级篇
- pr、ps、ae-安装
- 金蝶旗舰版,如何安装到MAC系统中
- win2008R2 像CA证书服务器(Linux)申请CA证书
- DOS运行GHOST加参数
- 【金融项目】尚融宝项目(十六)
- ale插件 vim_vim 撸码必备插件之 autoformat 与 ale[视频]
- 台式电脑如何设置此计算机,台式电脑怎么设置无线网络
- 全网最全的35个微信小游戏
- 使用NSData下载图片,但是没有下载成功
- C语言入门 -- 数字的两种读法(2021/1/7)
- 无损flac文件转mp3格式转换器如何选择?
- labview二维数组保存至excel