Spring JPA联表查询之OneToOne源码详解
作者:烟雨戏江南 发布时间:2022-08-06 13:57:55
前言
前面几篇我们学习的都是单表查询,就是对一张表中的数据进行查询。而实际项目中,基本都会有多张表联合查询的情况,今天我们就来了解下JPA的联表查询是如做的。
源码
@OneToOne 注解实现一对一关系映射。比如用户跟车辆的关系(这里假设一个人只能有一辆车),一个用户只能对应一辆车,而一辆车同样只能对应一个用户。
老规矩,在实例之前,我们先看看源码:
public @interface OneToOne {
Class targetEntity() default void.class;
CascadeType[] cascade() default {};
FetchType fetch() default EAGER;
String mappedBy() default "";
boolean orphanRemoval() default false;
}
注解属性
targetEntity:(可选)表示默认关联的实体类型,默认为当前标注的实体类
cascade:(可选)当前类对象操作后级联对象的操作。默认为不级联任何操作。
fetch:(可选)关联是否延迟加载或者立刻加载。立刻加载是立刻获取关联的实体;延迟加载是表示关系类在被访问时才加载。默认值EAGER,也就是立刻加载。
mappedBy:(可选)拥有关联关系的域,如果关系是单向的就不需要;如果是双向关系表,那么拥有关系的这一方有建立、解除和更新与另一方关系的能力,而另一方没有,只能被动管理,这个属性被定义在关系的被拥有方
orphanRemoval:(可选)是否将删除操作应用于具有已从关系中删除,并将删除操作级联到这些实体。
单向联表
我这里所说的单向联表就是只有一方添加注解;通俗讲就是我可以通过 user 获取到其 car 的信息,而不同通过 car 获取到其 user 的信息。
user 实体类
@Entity
@Data
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
private int age;
@OneToOne
@JoinColumn(name = "car_id")
private Car car;
}
car 实体类
@Data
@Entity
public class Car {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
}
执行请求 /user/findById?id=1
,控制台打印如下:
Hibernate:
select
user0_.id as id1_1_0_,
user0_.age as age2_1_0_,
user0_.car_id as car_id4_1_0_,
user0_.name as name3_1_0_,
car1_.id as id1_0_1_,
car1_.name as name2_0_1_
from
user user0_
left outer join
car car1_
on user0_.car_id=car1_.id
where
user0_.id=?
查询结果
Optional[User(id=1, name=lili, age=11, car=Car(id=1, name=苏A00001))]
从上面的 JPQL 语句我们可以发现,uer 表 和 car 表 是通过left outer jion
进行连接的。这样我们可以通过查询 user 信息来获取其使用的 car 信息。
双向联表
我们除了需要通过 user 信息来获取其 car 信息为,有时还需要通过 car 信息来获取其 user 信息。只要再在 car 的实体中添加 user 字段,并添加@OneToOne
注解。(这里有一个堆栈溢出的问题需要注意一下,详情请见 JPA 错题集 第二个报错信息)
user 实体
@Entity
@Data
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
private int age;
@OneToOne
@JoinColumn(name = "car_id")
@JsonIgnore
private Car car;
}
car 实体
@Entity
@Data
public class Car {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
@OneToOne
@JoinColumn(name = "user_id")
@JsonIgnore
private User user;
}
执行请求 /car/findById?id=1
,控制台打印如下:
Hibernate:
select
car0_.id as id1_0_0_,
car0_.name as name2_0_0_,
car0_.user_id as user_id3_0_0_,
user1_.id as id1_2_1_,
user1_.age as age2_2_1_,
user1_.car_id as car_id4_2_1_,
user1_.name as name3_2_1_,
car2_.id as id1_0_2_,
car2_.name as name2_0_2_,
car2_.user_id as user_id3_0_2_
from
car car0_
left outer join
user user1_
on car0_.user_id=user1_.id
left outer join
car car2_
on user1_.car_id=car2_.id
where
car0_.id=?
查询结果
Optional[Car(id=1, name=苏A00001, user=com.example.sbjdemo.pojo.User@1c0775ea)]
用户信息可以通过后面的实体类获取
延迟加载(懒加载)
上面是 OneToOne 的情形,所以请求出来的数据也是比较有限的。试想一下,如果是 OneToMany,而我们只想获取 one 的信息,但是 many 的数据却跟着一起请求出来的,无论是从数据上还是性能上来讲,都是一种负担。那么,有没有一种方法,让我们能够只获取 one 的信息,而对于 many 可以动态获取呢?这时,我们想到了源码中一个 fetch 方法,他好像可以使用 LAZY 属性来控制数据懒加载。废话少说,上码。
user 实体
User 实体类上的 Car 属性中 @OneToOne
添加属性 fetch = FetchType.LAZY
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "car_id")
@JsonIgnore
private Car car;
执行请求 /user/findById?id=1
,控制台打印如下:
Hibernate:
select
user0_.id as id1_2_0_,
user0_.age as age2_2_0_,
user0_.car_id as car_id4_2_0_,
user0_.name as name3_2_0_
from
user user0_
where
user0_.id=?
查询结果:
Optional[com.example.sbjdemo.pojo.User@30c311e6]
通过控制台可以看出,只有一个用户查询的 sql,结果也就是一个 user 的实体类数据。那我们的 car 信息该如何查看呢?其实我们可以使用查询结果 result 去 get 我们所需要的数据,如下图所示:
查询完会发现,控制台又打印了一个 JPQL:
Hibernate:
select
car0_.id as id1_0_0_,
car0_.name as name2_0_0_,
car0_.user_id as user_id3_0_0_,
user1_.id as id1_2_1_,
user1_.age as age2_2_1_,
user1_.car_id as car_id4_2_1_,
user1_.name as name3_2_1_
from
car car0_
left outer join
user user1_
on car0_.user_id=user1_.id
where
car0_.id=?
恍然大悟,原来还是通过 sql 去完成的。
最后结论
oneToOne 主要针对一对一的场景,他们双方都可以作为维护端或者被维护端,所以 mappedBy 这个属性可有可无。这也要是与其他三个映射关系不同的地方。
来源:https://juejin.cn/post/7225879698952224828
猜你喜欢
- 我们平时使用的一些常见队列都是非阻塞队列,比如PriorityQueue、LinkedList(LinkedList是双向链表,它实现了De
- 1、异常分类通常分为三类:系统异常(SystemException),业务异常(BusinessException)和其他异常(Except
- 本文实例为大家分享了两种java实现FTP文件上传下载的方式,供大家参考,具体内容如下第一种方式:package com.cloudpowe
- 使用maven引入jar<dependency> <groupId>com.itextpdf</g
- 使用ExecutorService来停止线程服务之前的文章中我们提到了ExecutorService可以使用shutdown和shutdow
- 加载资源文件比较常用的有两种:一、用ClassLoader,说到这里就不得不提一下ClassLoader的分类,java内置的ClassLo
- 实例如下:import java.lang.reflect.Field;import java.lang.reflect.Invocatio
- 本文实例为大家分享了java实现短信验证码5分钟有效时间,供大家参考,具体内容如下实现一个发送短信验证码的请求,要求5分钟之内重复请求,返回
- 1.char数组(字符数组)->字符串可以通过:使用String.copyValueOf(charArray)函数实现。举例:char
- 最近在开发的过程当中,对于已有的代码,想将相关类绘制成UML类图,虽然现在有很多UML类图的优秀软件,比如ProcessOn(可视化编辑)、
- DATAXDataX 是阿里巴巴集团内被广泛使用的离线数据同步工具/平台,实现包括 MySQL、Oracle、SqlServer、Postg
- 本文实例为大家分享了java实现文件夹解压和压缩的具体代码,供大家参考,具体内容如下效果实现多个文件以及文件夹的压缩和解压代码分析impor
- 前言java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也不能做出来非常好用,
- 一、创建web项目1、打开idea软件,点击界面上的Create New Project2、进入如下界面。选中 java Enterpris
- 快速幂取模算法的引入是从大数的小数取模的朴素算法的局限性所提出的,在朴素的方法中我们计算一个数比如5^1003%31是非常消耗我们的计算资源
- 一、AOP概述AOP,即面向切面编程,简单来说就是将代码中重复的部分抽取出来,在需要执行的时候使用 * 的技术,在不修改源码的基础上对方法
- 一、前言前面我们学习了多态中的转型,那么现在我们开始学习抽象类的概述和使用二、抽象类生活大多事物是具有抽象含义的,比如我说一个生物,你想不到
- Solr我还是个菜鸟,写这一些文章只是记录一下最近一段时间学习Solr的心得。 Solr是什么? 最近我学Solr的时候,一直看到一句话,S
- 前言本文章主要从spring security安全认证登录内部调用流程来流程分析登录过程。一、登录时序图时序原图二、配置与代码1.引入库po
- 前言SQL注入漏洞作为WEB安全的最常见的漏洞之一,在java中随着预编译与各种ORM框架的使用,注入问题也越来越少。新手代码审计者往往对J