Spring Boot 61:JPA 中的级联类型

news2024/11/26 9:56:08

Spring Boot 61:JPA 中的级联类型

spring boot

图源:简书 (jianshu.com)

关系型数据库的增删改查操作会因为有关联关系而存在“级联操作”的需要,体现在 JPA 中,就是实体中会定义的级联类型(Cascade Type)。

JPA 中的级联类型由枚举jakarta.persistence.CascadeType表示,包括:

  • ALL
  • PERSIST
  • MERGE
  • REMOVE
  • REFRESH
  • DETACH

这些级联类型对应实体对象的状态转换操作,具体可以参考这篇文章。

ALL包含其他所有的操作。

下面详细说明这些级联类型的用途和影响。

示例

本文将使用以下的示例说明级联操作的影响:

@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "student")
public class Student {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotNull
    @NotBlank
    @Length(max = 45)
    @Column(unique = true)
    private String name;

    @OneToMany(mappedBy = "student",
            fetch = FetchType.LAZY)
    @Builder.Default
    private List<Email> emails = new ArrayList<>();

    public Student addEmail(Email email){
        if (this.emails.contains(email)){
            return this;
        }
        this.emails.add(email);
        email.setStudent(this);
        return this;
    }
}

@Accessors(chain = true)
@Setter
@Getter
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "email", uniqueConstraints = @UniqueConstraint(columnNames = {"name", "domain"}))
public class Email {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotNull
    @NotBlank
    @Length(max = 45)
    @EqualsAndHashCode.Include
    private String name;
    @NotBlank
    @NotNull
    @Length(max = 45)
    @EqualsAndHashCode.Include
    private String domain;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "student_id")
    private Student student;
}

这里包含两个实体,一个学生实体实例对应多个电子邮件实体实例。

关于一对多关系的更多介绍可以阅读这篇文章。

PERSIST

如果实体之间的关系不包含任何级联类型,添加一个实体时不会对另一个实体产生任何影响,换言之,添加学生实体实例后,只会插入学生相关表数据,电子邮件表不会有任何数据添加。

如果希望进行“级联添加”,需要使用级联类型CascadeType.PERSIST

public class Student {
    // ...
    @OneToMany(mappedBy = "student",
               cascade = CascadeType.PERSIST,
               fetch = FetchType.LAZY)
    private List<Email> emails = new ArrayList<>();	
}

现在,添加新的Student实例时,就会一同添加相关的Email实例到数据库。

测试用例:

students.forEach(s -> {
    session.persist(s);
});

SQL 日志:

insert into student (name) values (?)
binding parameter [1] as [VARCHAR] - [icexmoon]
insert into email (domain,name,student_id) values (?,?,?)
binding parameter [1] as [VARCHAR] - [qq.com]
binding parameter [2] as [VARCHAR] - [icexmoon]
binding parameter [3] as [BIGINT] - [1]
insert into email (domain,name,student_id) values (?,?,?)
binding parameter [1] as [VARCHAR] - [qq.com]
binding parameter [2] as [VARCHAR] - [123]
binding parameter [3] as [BIGINT] - [1]
...

当然,JPA 的 persist API 还包含对持久实体的更新操作,此时同样适用CascadeType.PERSIST级联类型:

var icexmoon = students.stream().filter(s -> 		s.getName().equals("icexmoon")).findFirst().get();
long id = icexmoon.getId();
var savedIcexmoon = session.find(Student.class, id);
var icexmoonEmail = savedIcexmoon.getEmails().get(0);
icexmoonEmail.setName("111")
    .setDomain("gmail.com");
session.persist(icexmoonEmail);

SQL 日志:

update email set domain=?,name=?,student_id=? where id=?
binding parameter [1] as [VARCHAR] - [gmail.com]
binding parameter [2] as [VARCHAR] - [111]
binding parameter [3] as [BIGINT] - [1]
binding parameter [4] as [BIGINT] - [1]

当然,使用JPARepository先关的 API 同样是可以的:

studentRepository.saveAndFlush(student);
student.getEmails().get(0)
    .setName("111")
    .setDomain("gmail.com");
studentRepository.saveAndFlush(student);

同样会进行级联插入/更新。

MERGE

CascadeType.MERGE对应 JPA 持久化上下文的merge操作。

示例:

public class Student {
    // ...
    @OneToMany(mappedBy = "student",
            cascade = CascadeType.MERGE,
            fetch = FetchType.LAZY)
    private List<Email> emails = new ArrayList<>();
}

调用示例:

var savedIcexmoon = session.find(Student.class, icexmoon.getId());
session.evict(savedIcexmoon);
savedIcexmoon.getEmails().get(0).setName("111").setDomain("gmail.com");
session.merge(savedIcexmoon);

SQL 日志:

update email set domain=?,name=?,student_id=? where id=?
binding parameter [1] as [VARCHAR] - [gmail.com]
binding parameter [2] as [VARCHAR] - [111]
binding parameter [3] as [BIGINT] - [1]
binding parameter [4] as [BIGINT] - [1]

REMOVE

CascadeType.REMOVE对应持久化上下文的remove操作。

示例:

public class Student {
    // ...
    @OneToMany(mappedBy = "student",
            cascade = {CascadeType.MERGE,
                    CascadeType.PERSIST,
                    CascadeType.REMOVE},
            fetch = FetchType.EAGER)
    private List<Email> emails = new ArrayList<>();
}

调用示例:

var savedIcexmoon = session.find(Student.class, icexmoon.getId());
session.remove(savedIcexmoon);

SQL 日志:

select s1_0.id,s1_0.name,e1_0.student_id,e1_0.id,e1_0.domain,e1_0.name from student s1_0 left join email e1_0 on s1_0.id=e1_0.student_id where s1_0.id=?
binding parameter [1] as [BIGINT] - [1]
delete from email where id=?
binding parameter [1] as [BIGINT] - [1]
delete from email where id=?
binding parameter [1] as [BIGINT] - [2]
delete from student where id=?
binding parameter [1] as [BIGINT] - [1]
...

DETACH

CascadeType.DETACH对应持久化上下文的detachevict操作,这些操作可以将持久实体从持久上下文中移除,变成分离实体。

evictdetach操作的别名,两者没有什么区别。

示例:

public class Student {
	// ...
    @OneToMany(mappedBy = "student",
               cascade = {CascadeType.MERGE,
                          CascadeType.PERSIST,
                          CascadeType.REMOVE,
                          CascadeType.DETACH},
               fetch = FetchType.EAGER)
    private List<Email> emails = new ArrayList<>();
}

调用示例:

var savedIcexmoon = session.find(Student.class, icexmoon.getId());
Assertions.assertTrue(session.contains(savedIcexmoon));
savedIcexmoon.getEmails().forEach(e->{
    Assertions.assertTrue(session.contains(e));
});
session.detach(savedIcexmoon);
Assertions.assertFalse(session.contains(savedIcexmoon));
savedIcexmoon.getEmails().forEach(e->{
    Assertions.assertFalse(session.contains(e));
});

REFRESH

CascadeType.REFRESH对应持久化上下文从数据库中重新加载数据的操作,比如Session.refresh(...)

示例:

public class Student {
    // ...
    @OneToMany(mappedBy = "student",
               cascade = {CascadeType.MERGE,
                          CascadeType.PERSIST,
                          CascadeType.REMOVE,
                          CascadeType.DETACH},
               fetch = FetchType.EAGER)
    @Builder.Default
    private List<Email> emails = new ArrayList<>();
}

调用示例:

var savedIcexmoon = session.find(Student.class, icexmoon.getId());
savedIcexmoon.setName("lalala");
var savedEmail = savedIcexmoon.getEmails().get(0);
savedEmail.setName("666").setDomain("gmail.com");
var oldEmailName = savedEmail.getName();
var oldEmailDomain = savedEmail.getDomain();
Assertions.assertEquals("lalala", savedIcexmoon.getName());
Assertions.assertEquals("666", savedEmail.getName());
Assertions.assertEquals("gmail.com", savedEmail.getDomain());
session.refresh(savedIcexmoon);
Assertions.assertEquals("icexmoon", savedIcexmoon.getName());
Assertions.assertEquals(oldEmailName, savedEmail.getName());
Assertions.assertEquals(oldEmailDomain, savedEmail.getDomain());

可以看到,调用Session.refresh后,关联到Student上的Email实例也被重新加载。

此外,Hibernate 提供了一些独特于 JPA 的级联类型,这些类型由枚举类型org.hibernate.annotations.CascadeType表示,大部分不同的级联类型已经作废,剩余的与 JPA 不同的级联类型有CascadeType.LOCK,要使用这些类型可以参考这篇文章。

The End,谢谢阅读。

本文的完整示例代码可以从这里获取。

参考资料

  • Overview of JPA/Hibernate Cascade Types. | Baeldung
  • 从零开始 Spring Boot 49:Hibernate Entity Lifecycle - 红茶的个人站点 (icexmoon.cn)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/722548.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【二维属性+贪心+双指针】ABC 195 D

D - Shipping Center (atcoder.jp) 题意&#xff1a; 思路&#xff1a; 经典中的经典&#xff0c;二维属性&#xff0c;对于其中的一个关键字排序&#xff0c;然后双指针将合法的可行解放入容器中&#xff0c;再去容器中找最优解&#xff0c;用双指针是因为它具有单调性 这里…

18-Linux 常用命令

目录 1.ls PS&#xff1a;FinalShell设置背景和字体 2.pwd 3.cd PS&#xff1a;认识 Linux 目录结构——Linux 是一个树形目录结构 PS&#xff1a;绝对路径 vs 相对路径 PS&#xff1a;使用 tab 键补全 PS&#xff1a;使用 ctrl c 重新输入 4.touch PS&#xff1a;L…

基于梯度下降算法的无约束函数极值问题求解

基于梯度下降算法的无约束函数极值问题求解 1 知识预警1.1导数1.2偏导数1.3方向导数1.4梯度 2 梯度下降算法3 无约束函数极值问题求解3.1 算例13.1.1 Python编程求解3.1.2 求解结果与可视化 3.2 算例2 Rosenbrock函数3.2.1 Python编程求解3.2.2 求解结果与可视化 1 知识预警 1…

JSON多层级数据自动映射值优化

JSON多层级数据自动映射值优化 FieldMethodHandles结果分析 Spring boot装载模板代码工程中&#xff0c;JSON多层级数据自动映射值只是简单封装JsonPath&#xff0c;对DTO的声明字段做foreach轮询&#xff0c;检查字段注解JPath&#xff0c;然后从JsonPath的解析缓存中读取JPat…

数字孪生三维可视化在海洋能源开发中的应用有哪些?

数字孪生是一种将现实世界中的实体、系统或过程在虚拟世界中重构&#xff0c;并相互映射和交互的领先技术。在能源领域&#xff0c;数字孪生技术可谓大有作为&#xff0c;比如通过实时监测和分析各种数据&#xff0c;提高能源的利用效率、降低能源成本、优化能源分配&#xff0…

【零基础入门学习Python---Python中机器学习和人工智能之快速入门实践】

&#x1f680; 零基础入门学习Python&#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜…

android_mars老师_获取用户定位

结果展示 MainActivity package com.example.locationmanager;import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity;import android.annotation.SuppressLint; import android.content.Context; import android.location.Location; import …

JavaWeb 笔记——3

JavaWeb 笔记——3 JavaWeb技术栈一、HTTP1.1、HTTP介绍1.2、HTTP请求数据格式1.3、HTTP响应数据格式 二、Web服务器 - Tomcat2.1、简介&基本使用2.2、Tomcat配置和部署项目2.3、Web项目结构2.4、创建MavenWeb项目2.5、IDEA集成本地Tomcat2.6、Tomcat-Tomcat Maven插件 三、…

23西安电子科技大学通信工程学院811考研录取情况

01、通信工程学院各个方向 02、23通信工程学院一志愿考研录取情况总览、平均分 PS&#xff1a;通院23年院线相对于22年院线上涨5-15分&#xff0c;个别专业下降10分反应西电通院热度23年和22年基本一致。 PS&#xff1a;1、通院23年比较多的考生在本部学硕、专硕扎堆&#xff…

【花雕】全国青少年机器人技术一级考试备考实操搭建手册6

随着科技的不断进步&#xff0c;机器人技术已经成为了一个重要的领域。在这个领域中&#xff0c;机械结构是机器人设计中至关重要的一部分&#xff0c;它决定了机器人的形态、运动方式和工作效率。对于青少年机器人爱好者来说&#xff0c;了解机械结构的基础知识&#xff0c;掌…

yolov7论文学习——创新点解析、网络结构图

创新点 1、提出了E-ELAN&#xff0c;但是只在yolov7-e6e中使用到。 2、yolov7基于拼接模型的缩放方法&#xff0c;在yolov7x中使用到。 3、将重参数化卷积应用到残差模块中或者用到基于拼接的模块中去。RepConvN 4、提出了两种新的标签分配方法 一、ELAN和E-ELAN 1、 ELAN …

AI 如何应对 DevOps 监控和可观察性挑战

持续监控和可观察性用例 CI异常检测&#xff1a; AI可以分析历史数据以检测持续集成阶段的异常。任何不寻常的变化都可以在进入下一阶段之前进行标记以供审查。IBM Watson AnomalyDetection 等工具可以通过使用 AI 检测模式和异常情况来帮助识别这些异常情况。 代码质量保证&…

如何快速定位linux故障

1、背景 有时候会遇到一些疑难杂症&#xff0c;并且监控插件并不能一眼立马发现问题的根源。这时候就需要登录服务器进一步深入分析问题的根源。那么分析问题需要有一定的技术经验积累&#xff0c;并且有些问题涉及到的领域非常广&#xff0c;才能定位到问题。所以&#xff0c…

防火墙详解

1、什么是防火墙&#xff1f; 防火墙&#xff08; Firewall &#xff09;是防止火灾发生时&#xff0c;火势烧到其它区域&#xff0c;使用由防火材料砌的墙。 后来这个词语引入到了网络中&#xff0c;把从外向内的网络入侵行为看做是火灾&#xff0c;防止这种入侵的策略叫做防…

批量多开谷歌浏览器丨非扩展chrome浏览器实现分身多开微博 切换多个微博帐号工具

教你多开用Google 浏览器 实现Chrome怎样同时登录多个微博号 按照此教程多开后的Google浏览器可以实现相互的独立性&#xff0c;每个浏览器上收藏的书签、增加的拓展程序都可以实现独立性并可实现独立记忆性 一、安装正版Google浏览器 1&#xff1a;安装位置最好选择非C盘 二…

基于低代码平台打造的焙乐道销售支持系统

编者按&#xff1a;低代码平台说了那么多&#xff0c;在实际应用中又是怎样体现的它的种种优势呢&#xff1f;今天小编结合实际案例来说说。 本文是以最大的烘焙原料产商——焙乐道的销售支持系统为例子&#xff0c;进行说明。 客户说明&#xff1a;焙乐道是一家国际性集团公司…

凝思系统docker离线安装

# linux离线安装docker (18.03.1-ce) ## 解压&#xff0c;得到docker文件夹 tar xzvf docker-18.03.1-ce.tgz ## 将docker文件夹里面的所有内容复制到/usr/bin目录 sudo cp docker/* /usr/bin/ ## 开启docker守护进程 sudo dockerd & 当终端中显示【API list…

有参构造,无参构造,半缺省构造

目录 有参构造 无参构造 半缺省构造 有参构造 C 中的有参构造函数&#xff08;Parameterized Constructor&#xff09;是一个类中带有参数的特殊成员函数&#xff0c;用于创建对象并根据传入的参数对对象的成员进行初始化。有参构造函数在定义时需要指定参数的类型和名称&am…

相机- yolo训练集 环境搭建

一、环境准备 运行cmd执行python --version 检查是否安装成功 安装pip&#xff0c;打开运行指令 python -m ensurepip --upgrade 打开官网&#xff0c;下载get_pip.py 运行cmd 运行指令python get-pip.py 运行cmd 运行指令 pip --version 显示pip版本即安装成功 根据上面获…

Lucene介绍与入门使用

https://github.com/apache/lucene Lucene简介 Lucene是apache软件基金会4 jakarta项目组的一个子项目&#xff0c;是一个开放源代码的全文检索引擎工具包&#xff0c;但它不是一个完整的全文检索引擎&#xff0c;而是一个全文检索引擎的架构&#xff0c;提供了完整的查询引擎…