分析了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,一对多删除详解相关推荐

  1. JPA Spring Data JPA详解

    JPA & Spring Data JPA 一.JPA 1. JPA是什么 JPA(Java Persistence API)Java持久化 API,是一套基于ORM思想的规范. ORM(Ob ...

  2. Spring Data JPA OneToMany注解参数orphanRemoval,一对多删除详解

    博主:爱码叔 个人博客站点: icodebook 公众号:漫话软件设计 专注于软件设计与架构.技术管理.擅长用通俗易懂的语言讲解技术.对技术管理工作有自己的一定见解.文章会第一时间首发在个站上,欢迎大 ...

  3. hql实例 jpa_SpringBoot学习笔记九:Spring Data Jpa的使用

    Spring Data Jpa 简介 JPA JPA(Java Persistence API)意即Java持久化API,是Sun官方在JDK5.0后提出的Java持久化规范(JSR 338,这些接口 ...

  4. 【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.引入依赖 ...

  5. Spring Boot(五):spring data jpa的使用

    Spring Boot(五):spring data jpa的使用 一.spring data jpa介绍 1,JPA是什么 JPA(Java Persistence API)是Sun官方提出的Jav ...

  6. spring-boot (三) spring data jpa

    学习文章来自:http://www.ityouknow.com/spring-boot.html spring data jpa介绍 首先了解JPA是什么? JPA(Java Persistence ...

  7. 干货|一文读懂 Spring Data Jpa!

    有很多读者留言希望松哥能好好聊聊 Spring Data Jpa!其实这个话题松哥以前零零散散的介绍过,在我的书里也有介绍过,但是在公众号中还没和大伙聊过,因此本文就和大家来仔细聊聊 Spring D ...

  8. Spring Data JPA例子[基于Spring Boot、Mysql]

    关于Spring Data Spring社区的一个顶级工程,主要用于简化数据(关系型&非关系型)访问,如果我们使用Spring Data来开发程序的话,那么可以省去很多低级别的数据访问操作,如 ...

  9. JPA、Hibernate和Spring Data JPA区别

    大家好,我是神韵,是一个技术&生活博主.出文章目的主要是两个,一是好记忆不如烂笔头,记录总结中提高自己.二是希望我的文章可以帮到大家.欢迎来点赞打卡,你们的行动将是我无限的动力. 本篇主题是: ...

最新文章

  1. 切换Python2和Python3的方法
  2. 【fjwc2015】世界树
  3. android 程序退出广播,android 利用广播实现程序的强制退出
  4. linux测试地址是否能访问_一个小测试能看出孩子注意力是否集中,提前弥补,上小学会很轻松...
  5. Oracle DBA必须学会的11个Linux基本命令
  6. 关于徒手脱壳的几种方法
  7. 20200501:力扣185周赛上
  8. SQL JOIN--初级篇
  9. pr、ps、ae-安装
  10. 金蝶旗舰版,如何安装到MAC系统中
  11. win2008R2 像CA证书服务器(Linux)申请CA证书
  12. DOS运行GHOST加参数
  13. 【金融项目】尚融宝项目(十六)
  14. ale插件 vim_vim 撸码必备插件之 autoformat 与 ale[视频]
  15. 台式电脑如何设置此计算机,台式电脑怎么设置无线网络
  16. 全网最全的35个微信小游戏
  17. 使用NSData下载图片,但是没有下载成功
  18. C语言入门 -- 数字的两种读法(2021/1/7)
  19. 无损flac文件转mp3格式转换器如何选择?
  20. labview二维数组保存至excel

热门文章

  1. 算法导论 思考题 9-3
  2. Bootargs参数详解
  3. html判断是否为文件夹,FSO(fileSystemObject)判断文件及文件夹是否存在
  4. 【项目一】基于Qt平台的交互式输入与输出窗口
  5. UEFI Shell命令详解,自写一个UEFI Shell命令
  6. linux shell中 '' ``的区别
  7. MVC MVP MVVP Android端构建个人总结
  8. python+appium
  9. 【配电网优化】基于粒子群算法实现GARVER-6节点配电网络直流潮流计算附matlab代码
  10. Java 设计模式 Iterator 迭代 模式