从零开始 Spring Boot 68:连接实体

news2024/11/29 2:46:41

从零开始 Spring Boot 68:连接实体

spring boot

图源:简书 (jianshu.com)

在 JPA 中关联实体实际上对应表连接,而表连接可以通过内连接(Inner Join)、外连接(Outer Join)和 Where等方式实现,实际上 JPA 也用这些方式实现对所关联的实体数据的查询和加载。

本文示例使用以下实体类:

@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Entity
public class School {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

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

    @OneToMany(mappedBy = "school", cascade = CascadeType.ALL)
    @Builder.Default
    private List<Student> students = new ArrayList<>();

    public School addStudent(Student student){
        this.students.add(student);
        student.setSchool(this);
        return this;
    }
}

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

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

    @Setter
    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "school_id")
    private School school;

    @OneToMany(mappedBy = "student",
            cascade = CascadeType.ALL)
    @Builder.Default
    private List<Email> emails = new ArrayList<>();

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

@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(uniqueConstraints = @UniqueConstraint(columnNames = {"account", "domain"}))
public class Email {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotNull
    @NotBlank
    @Length(max = 45)
    private String account;
    @NotNull
    @NotBlank
    @Length(max = 45)
    private String domain;

    @Setter
    @ManyToOne
    @JoinColumn(name = "student_id")
    private Student student;
}

隐式连接

因为在示例中已经创建了实体之间的对应关系,所有如果查询涉及关联的实体,查询的时候 JPA 会隐式地使用join SQL 进行表连接以获取数据:

var school = session.createQuery("select s.school from Student s where s.name=:name", School.class)
    .setParameter("name", "icexmoon")
    .getSingleResult();
Assertions.assertEquals("霍格沃茨魔法学校", school.getName());

SQL 日志:

select s2_0.id,s2_0.name from student s1_0 join school s2_0 on s2_0.id=s1_0.school_id where s1_0.name=?
binding parameter [1] as [VARCHAR] - [icexmoon]

显式内连接

当然,也可以在 JPQL 中显式地使用join语句进行内连接:

List<Student> students = session.createQuery("select s from Student s join Email e on e.student=s where e.domain=:domain", Student.class)
    .setParameter("domain", "gmail.com")
    .getResultList();

SQL 日志:

select s1_0.id,s1_0.name,s1_0.school_id from student s1_0 join email e1_0 on e1_0.student_id=s1_0.id where e1_0.domain=?
binding parameter [1] as [VARCHAR] - [gmail.com]

在 JPQL 中,join实际上指的就是inner join(内连接),所以inner是可选的:

List<Student> students = session.createQuery("select s from Student s inner join Email e on e.student=s where e.domain=:domain", Student.class)
    .setParameter("domain", "gmail.com")
    .getResultList();

查询集合

比较特殊的是,如果查询的结果是集合(Collection),getResultList返回的是将这些集合元素合并后的结果:

List<Email> emails = session.createQuery("select s.emails from Student s where s.name=:name", Email.class)
    .setParameter("name", "icexmoon")
    .getResultList();

在这个示例中,s.emails的类型实际上是List,但返回的结果并不是List<List>List<Collection>,而是List<Email>,实际上是将多个List内的元素合并后的结果。

如果需要对返回的集合添加条件,需要使用join

List<Email> emails = session.createQuery("select e from Student s" +
                                         " join Email e on e.student=s" +
                                         " where s.name=:name" +
                                         " and e.domain=:domain",
                                         Email.class)
    .setParameter("name", "icexmoon")
    .setParameter("domain", "qq.com")
    .getResultList();

当然也可以用 Java 遍历结果并筛选。

外连接

JPA 只支持左外连接(left join),不支持right join

List<Student> students = session.createQuery("select s from Student s left join Email e" +
                                             " on e.student=s" +
                                             " where e.domain=:domain", Student.class)
    .setParameter("domain", "qq.com")
    .getResultList();

如果需要使用右外连接,可以调换left join两边的实体对象位置。

连接多个实体

和 SQL 中可以连接多张表一样,使用join可以在 JPQL 中连接多个实体:

List<Student> students = session.createQuery("select s from School sc" +
                                             " left join Student s on s.school=sc" +
                                             " left join Email e on e.student=s" +
                                             " where sc.name=:scname" +
                                             " and e.domain=:domain", Student.class)
    .setParameter("scname", "霍格沃茨魔法学校")
    .setParameter("domain", "qq.com")
    .getResultList();

where

当不存在外键关系时,用where进行关联很有用:

var students = session.createQuery("select s from Student s,Email e" +
                                   " where s=e.student" +
                                   " and e.domain=:domain", Student.class)
    .setParameter("domain", "gmail.com")
    .getResultList();

特别的,如果用where连接实体,且没有指定连接条件时,实际上查询结果是“笛卡尔积”:

var students = session.createQuery("select s from Student s,Email e", Student.class)
    .getResultList();

实际执行的 SQL 是:

select s1_0.id,s1_0.name,s1_0.school_id from student s1_0,email e1_0

因为是笛卡尔积,这会对两张关联表都进行全表遍历,性能会很差。

因为实体之间有明确的关联关系,所以这里Studentemails属性会被 Hibernate 用额外查询进行关联,所以最后获取到的Student对象不能体现笛卡尔积的查询结果。

The End,谢谢阅读。

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

参考资料

  • JPA Join Types | Baeldung

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

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

相关文章

系统运维和网络运维有什么区别吗?

跟着互联网以及科技的高速开展&#xff0c;衍生出了许多的新奇职业&#xff0c;比方网络运维、网络安全运维。 从字面意思了解&#xff0c;两者之间没有什么太大区别&#xff0c;因而很多人很容易将两者混杂。 系统和网络运维有什么区别? 一个偏系统&#xff08;linux、doc…

cookiesession(会话技术)

会话技术 Http:超文本传输协议&#xff0c;不可靠。 ​ 不保持连接的协议。无状态的。 什么是会话 会话是在一次会话中包含多次请求和响应 一次会话&#xff1a;浏览器第一次给服务器资源发送请求&#xff0c;会话建立&#xff0c;直到有一方断开为止 会话的作用 由于Http是…

微服务之Eureka服务注册中⼼

关于务注册中⼼服 服务注册中⼼本质上是为了解耦服务提供者和服务消费者,尽可能量使两者联系可控在一定的范围外 1.在父项目下下引入 Spring Cloud 依赖 <dependencyManagement> <dependencies> <!-- SCN --> <dependency> <groupId> org.sp…

【记录】SMB|Windows下修改SMB端口并挂载

环境&#xff1a;Window11 使用背景&#xff1a;勒索病毒导致445端口不安全&#xff0c;故而该端口在服务器端被全面禁用了&#xff0c;如需使用SMB服务需要换个SMB服务端口。 方法1&#xff1a;端口转发 winx点开管理员权限的终端&#xff1a; 运行以下指令&#xff0c;检查…

ROS:URDF使用

目录 一、URDF集成Rviz&#xff08;实例讲解&#xff09;1.1需求1.2流程1.3code操作1.3.1创建功能包1.3.2编写 URDF 文件1.3.3在 launch 文件中集成 URDF 与 Rviz1.3.4在 Rviz 中显示机器人模型1.3.5优化 rviz 启动 二、URDF语法详解2.1robot2.1.1属性2.2.2子标签 2.2link2.2.1…

手把手教你玩转内存函数(含模拟实现)

目录 一、memcpy 1.认识memcpy 2.使用memcpy 3.拓展&#xff1a;模拟实现memcpy 二、memmove 1.认识memmove 2.使用memmove 3.拓展&#xff1a;模拟实现memmove 三、memcmp 1.认识memcmp 2.使用memcmp 四、memset 1.认识memset 2.使用memset 3.拓展&#xff1a;模…

【网络编程】应用层协议——HTTP协议

文章目录 一、HTTP协议基本认识二、URL的认识2.1 urlencode和urldecode 三、HTTP协议格式3.1 HTTP请求与响应格式3.2 如何保证请求和响应被应用层完整读取&#xff1f;3.3 请求和响应如何做到序列化和反序列化&#xff1f;3.4 代码验证请求格式3.5 代码验证响应格式3.5.1 telne…

小程序开发及生态丰富,还需要App吗?

微信小程序自2017年推出以来&#xff0c;其生态系统得到了迅速的发展和壮大。作为中国最大的社交平台之一&#xff0c;微信拥有庞大的用户基础。微信小程序作为微信生态系统的一部分&#xff0c;自然而然地吸引了大量用户。据对公开资料进行统计&#xff0c;2021年全网小程序数…

校园跑腿小程序:为学生提供便捷的服务

随着社会的发展和人们生活水平的提高&#xff0c;高等教育越来越受到重视。大学校园不仅是学习的地方&#xff0c;也是学生们日常生活的场所。然而&#xff0c;在繁忙的学业和生活压力下&#xff0c;学生可能经常面临诸如代购、快递、取餐等各种琐碎但繁琐的任务。基于这个需求…

ModaHub魔搭社区:AI原生云向量数据库Zilliz Cloud与 Cohere 集成搭建智能问答系统

目录 准备工作 主要参数 准备数据 创建 Collection 插入数据 测试问答 本文将演示如何使用 Zilliz Cloud 和 Cohere 搭建基于 SQuAD 数据集 的问答系统。其中,Zilliz Cloud 负责提供向量数据库,Cohere 负责提供获取指定文字向量表示的接口。 准备工作 本示例中的脚…

黑马前端三大件

文章目录 1.html1.1 标题标签1.2 段落标签1.3换行标签1.4 水平分割线标签1.5 文本格式化标签1.6 图片标签1.7 音频标签1.8 视频标签1.9 链接标签的介绍1.10列表标签1.10.1 无序列表1.10.2 无序列表1.10.3 自定义列表 1.11表格属性1.11.1表格的基本标签1.11.2 表格相关属性1.11.…

使用layui滑块slider遇到问题解决:1、加标尺2、兼容移动端拖拽1、多个滑块使用赋值4、切换箭头赋值问题

使用layui框架 滑块组件 slider 时遇到的问题&#xff0c;以下图为例&#xff0c;我创建了一个总滑块和3个滑块&#xff0c;改变总滑块可以控制滑块123&#xff1a; 1、我的需求是加上标尺&#xff0c;layui没有该功能&#xff0c;自己写了一个简单的,代码在下面。 2、移动端…

Linux:输入输出流、重定向、管道命令

相关文章 Linux&#xff1a;chgrp、chown、chmod权限属性更改指令 Linux&#xff1a; 磁盘状态观察命令lsblk、blkid Linux&#xff1a;df、du容量查询指令 1、标准流文件 数据流重定定向由字面上的意思来看&#xff0c;就是将数据定向到其他地方去&#xff0c;事实也是如此…

ModaHub魔搭社区:非结构化数据范式转变和示例

目录 范式转变——非结构化数据 非结构化数据示例 范式转变——非结构化数据 既然我们对结构化数据和半结构化数据有了清晰的理解,现在让我们开始谈谈非结构化数据。与结构化数据和半结构化数据不同,非结构化数据可以采取任何形式,可以有任意大小或尺寸,并需要大量的运行…

ModaHub魔搭社区:AI原生云向量数据库Zilliz Cloud与 HuggingFace 集成搭建问答系统

目录 准备工作 主要参数 创建 Collection 插入数据 测试问答 本文将演示如何使用 Zilliz Cloud 和 HuggingFace 搭建问答系统。其中,Zilliz Cloud 负责提供向量数据库,HuggingFace 负责提供获取指定文字向量表示的接口。 准备工作 本示例中的脚本需要安装 pymilvus,…

寡头竞争和混合策略均衡

纯寡头的产量竞争 两家企业生产相同的产品,产量是决策变量,市场上只有一种价格。市场需求/Demand:P = 100 - (Q1+Q2) Q 是企业生产的产量 假设生产成本为0 这个案例可能存在的结果 (1)合谋 (2)古诺-纳什均衡 每个企业将它竞争对手的产量水平视为固定的,然后决定自己…

【C++学习笔记】C++如何规范C语言中的类型转换

C的类型转换 1 C语言中类型转换的缺陷2 为什么C要规范C的类型转换3 C强制类型转换3.1 static_cast3.2 reinterpret_cast3.3 const_cast3.4 dynamic_cast 1 C语言中类型转换的缺陷 在C语言中&#xff0c;如果赋值运算符左右两侧类型不同&#xff0c;或者形参与实参类型不匹配&a…

【kubernetes系列】Kubernetes之RBAC

概述 k8s的权限控制在实际工作中不那么经常使用&#xff0c;但是却是很重要的&#xff0c;我们需要深入理解才能很好的解决某些问题。在我们现目前的了解中&#xff0c;常用的授权插件有以下几种&#xff1a; Node&#xff08;节点认证&#xff09; ABAC(基于属性的访问控制) …

【UE4 C++】03-新建UE C++工程,新建C++类

UE版本&#xff1a;4.26 步骤 新建一个空白模板工程&#xff0c;选择C项目&#xff0c;选择项目名和项目存储位置&#xff0c;然后点击创建项目。 新建C类 选择父类为Character 命名&#xff0c;选择公有&#xff0c;然后点击创建类 等待编译完成 此时在Visual Studio中可以看…

源启:云原生计算架构的行业实现

7月5日&#xff0c;由工业和信息化部网络安全产业发展中心、中国软件行业协会等单位指导&#xff0c;中国电子主办的“麒麟傲天聚创未来2023操作系统产业大会”在京举行。中电金信研究院副院长陈书华在大会上发表主旨演讲。 陈书华认为数字经济已成为社会发展的重要引擎&#…