Spring JPA联表查询之注解属性详解
作者:烟雨戏江南 发布时间:2021-11-04 14:19:04
前言
对于联表查询的四个注解 @OneToOne
、@OneToMany
、@ManyToOne
和 @ManyToMany
,他们有几个用得比较多的属性需要了解一下。
一、targetEntity
(可选)指定关联的实体类;默认为当前标注的实体类。
@JoinColumn(name = "car_id")
@OneToOne(fetch = FetchType.LAZY, targetEntity = Car.class)
private Car car;
二、cascade
(可选)当前类对象操作后级联对象的操作。默认为不级联任何操作。
1、不定义
只对作用的实体类有影响,对级联对象不会产生任何影响
2、CascadeType.PERSIST
级联新建。对父对象进行持久化,同时对子对象也相应的持久化。
user 实体类中关联的对象字段(级联对象)
@JoinColumn(name = "car_id")
@OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
@JsonIgnore
private Car car;
service 层新建方法
@GetMapping("/save")
public User save(String name, Integer age, String carname) {
User user = new User();
user.setName(name);
user.setAge(age);
Car car = new Car();
car.setName(carname);
car.setUser(user);
user.setCar(car);
return userService.save(user);
}
执行请求 /user/save?name=lala&age=21&carname=苏M00002
,控制台打印如下:
Hibernate:
insert
into
user
(age, car_id, name)
values
(?, ?, ?)
Hibernate:
update
car
set
name=?,
user_id=?
where
id=?
如果有级联新建(保存)的需求,只能使用 CascadeType.PERSIST
或者 CascadeType.ALL
,使用其他的操作会报错。
3、CascadeType.REMOVE
级联删除。删除数据库中的对应实体,同时删除对应的所有关联对象
user 实体类中关联的对象字段(级联对象)
@JoinColumn(name = "car_id")
@OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.REMOVE)
@JsonIgnore
private Car car;
service 层删除方法
/**
* 通过id进行删除数据
* @param id
*/
@GetMapping("/deleteById")
public void deleteById(Integer id){
userService.deleteById(id);
}
执行请求 /user/deleteById?id=17
,控制台打印如下:
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=?
[nio-7777-exec-5] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [INTEGER] - [17]
Hibernate:
select
car0_.id as id1_0_0_,
car0_.name as name2_0_0_,
car0_.user_id as user_id3_0_0_
from
car car0_
where
car0_.id=?
[nio-7777-exec-5] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [INTEGER] - [5]
Hibernate:
update
car
set
name=?,
user_id=?
where
id=?
[nio-7777-exec-5] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [VARCHAR] - [苏M00001]
[nio-7777-exec-5] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [INTEGER] - [null]
[nio-7777-exec-5] o.h.type.descriptor.sql.BasicBinder : binding parameter [3] as [INTEGER] - [5]
Hibernate:
delete
from
user
where
id=?
[nio-7777-exec-5] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [INTEGER] - [17]
Hibernate:
delete
from
car
where
id=?
[nio-7777-exec-5] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [INTEGER] - [5]
如果有级联删除的需求,只能使用 CascadeType.REMOVE
或者 CascadeType.ALL
;使用其他的虽然不会报错,但是只会对作用对象进行删除,相关联的数据不会进行删除。
4、CascadeType.REFRESH
级联刷新。作用对象 refresh
的同时级联对象也进行 refresh
。这里 refresh
方法是EntityManager
的方法,我们来看一下他的源码
/**
* Refresh the state of the instance from the database,
* overwriting changes made to the entity, if any.
* @param entity entity instance
* @throws IllegalArgumentException if the instance is not
* an entity or the entity is not managed
* @throws TransactionRequiredException if there is no
* transaction when invoked on a container-managed
* entity manager of type <code>PersistenceContextType.TRANSACTION</code>
* @throws EntityNotFoundException if the entity no longer
* exists in the database
*/
public void refresh(Object entity);
最主要的是从数据库中刷新实例的状态,覆盖对实体所做的更改(如果有的话)
;通俗讲就是,只要没有做持久化的数据更改,就得不到我的认可。
5、CascadeType.MERGE
级联更新。
user 实体类中关联的对象字段(级联对象)
@JoinColumn(name = "car_id")
@OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE)
@JsonIgnore
private Car car;
service 层更新方法
@GetMapping("/save")
public User save(Integer id, String name, Integer age, Integer carid, String carname) {
User user = new User();
if (id != null) user.setId(id);
user.setName(name);
user.setAge(age);
Car car = new Car();
if (carid != null) car.setId(carid);
car.setName(carname);
car.setUser(user);
user.setCar(car);
return userService.save(user);
}
执行请求/user/save?id=1&name=kiki&age=33&carid=1&carname=苏M00003
,控制台打印如下:
Hibernate:
select
user0_.id as id1_2_1_,
user0_.age as age2_2_1_,
user0_.car_id as car_id4_2_1_,
user0_.name as name3_2_1_,
car1_.id as id1_0_0_,
car1_.name as name2_0_0_,
car1_.user_id as user_id3_0_0_
from
user user0_
left outer join
car car1_
on user0_.car_id=car1_.id
where
user0_.id=?
[io-7777-exec-10] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [INTEGER] - [1]
Hibernate:
update
car
set
name=?,
user_id=?
where
id=?
[io-7777-exec-10] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [VARCHAR] - [苏M00003]
[io-7777-exec-10] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [INTEGER] - [1]
[io-7777-exec-10] o.h.type.descriptor.sql.BasicBinder : binding parameter [3] as [INTEGER] - [1]
Hibernate:
update
user
set
age=?,
car_id=?,
name=?
where
id=?
[io-7777-exec-10] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [INTEGER] - [33]
[io-7777-exec-10] o.h.type.descriptor.sql.BasicBinder : binding parameter [2] as [INTEGER] - [1]
[io-7777-exec-10] o.h.type.descriptor.sql.BasicBinder : binding parameter [3] as [VARCHAR] - [kiki]
[io-7777-exec-10] o.h.type.descriptor.sql.BasicBinder : binding parameter [4] as [INTEGER] - [1]
可以更新所有级联的对象,但是前提是要提供对象的 id,否则系统会认为你是新增。
6、CascadeType.ALL
表示同时选择 CascadeType.PERSIST
、CascadeType.REMOVE
、CascadeType.REFRESH
和 CascadeType.MERGE
。一般情况不会使用,因为 CascadeType.REMOVE
是个高危操作。
三、fetch
(可选)关联是否延迟加载(懒加载 FetchType.LAZY
)或者立刻加载(FetchType.EAGER
)。立刻加载是立刻获取关联的实体;延迟加载(懒加载)是表示关系类在被访问时才加载。
FetchType.LAZY
User 实体类上的 Car 属性中 @OneToOne
添加属性 fetch = FetchType.LAZY
(这里有一个报错需要注意一下,详情请见 # Spring JPA 错题集 第一个信息)
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "car_id")
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=?
[nio-7777-exec-1] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [INTEGER] - [1]
Hibernate:
select
car0_.id as id1_0_0_,
car0_.name as name2_0_0_
from
car car0_
where
car0_.id=?
[nio-7777-exec-1] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [INTEGER] - [1]
这里先挖一个坑,就是 JPA 在读取数据时 N+1 的问题。
FetchType.EAGER
再看看立刻加载的场景。
执行请求/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_,
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=?
[nio-7777-exec-2] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [INTEGER] - [1]
懒加载的时候(LAZY)只会将主体数据请求出来,对于级联数据需要我们主动 get,如下图所示:
而立刻加载的时候(EAGER)会将主体数据和所有级联数据请求出来,对于大数据的情况,系统可能会存在一定的压力,所以实际项目中,我们大多数情况下会使用 FetchType.LAZY
。
四、mappedBy
(可选)拥有关联关系的域,如果关系是单向的就不需要;如果是双向关系表,那么拥有关系的这一方有建立、解除和更新与另一方关系的能力,而另一方没有,只能被动管理,这个属性被定义在关系的被拥有方
五、orphanRemoval
(可选)是否将删除操作应用于具有已从关系中删除,并将删除操作级联到这些实体;默认为false。
来源:https://juejin.cn/post/7226303381261221946


猜你喜欢
- 本文实例为大家分享了C#用timer实现背单词小程序的具体代码,供大家参考,具体内容如下看到网上有类似的教程视频实现单词本,于是自己敲了一个
- Unity中的PostProcessBuild:深入解析与实用案例在Unity游戏开发中,我们经常需要在构建完成后对生成的应用程序进行一些额
- Springboot2.x的session和cookie有效期session和cookie的相关区别和联系就不介绍了,这里就记录一下笔记。背
- 前言HTML5 WebSocket实现了服务器与浏览器的双向通讯,双向通讯使服务器消息推送开发更加简单,最常见的就是即时通讯和对信息实时性要
- 1、JDK1.8之前:假设有实体类User,里面有字段id,我们将相同id的User进行分组,并存放在Map中。(例子不是很恰当,但很能说明
- 用来记录自己所用到的知识前两天在做项目的时候发现有时候在访问网络数据的时候由于后台要做的工作较多,给我们返回数据的时间较长,所以老大叫我加了
- <results>标签在Struts2的MVC框架的视图中所扮演的角色。动作是负责执行业务逻辑。执行业务逻辑后,接下来的步骤是使
- 用的Idea,在写MyBatis时,测试发现有以下的报错信息Error parsing SQL Mapper Configuration.
- 1.线程与进程进程是代码在数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,线程则是一个实体,一个进程中至少有一个线程,是CPU
- 前两天发现 idea 终于更新了2020.1版本,新增了好多的特性,这里不介绍,主要写一下中文插件的安装首先下载新版 安装包 https:/
- android多线程断点下载,带进度条和百分比显示,断点下载的临时数据保存到SD卡的文本文档中,建议可以保存到本地数据库中,这样可以提高存取
- 前言:$是c# 6.0 的语言特性,功能类似string.format(),更方便的地方在于不要像format一样使用索引,可以直接使用变量
- 背景公司最近要求给我负责的APP加上视频录制和发布的功能,我简单的完成了基本的录制和视频压缩功能,后来发现发布接口需要上传视频的截图,网上搜
- 处理提交数据1、提交的域名称和处理方法的参数名一致提交数据 : http://localhost:8080/hello?name=xiaoh
- Springboot自定义注解,支持SPEL表达式举例,自定义redis模糊删除注解1.自定义注解import java.lang.anno
- 专栏介绍本系列专栏会以虹猫蓝兔七侠传的故事为例来给大家详细分析所有模式,希望能给大家带来帮助!本期介绍模式: 简单工厂模式案例: 一个天外陨
- 一、单线程改造为多线程也是个技术活正如我们看到耗子叔叔博客里写的那样,原来是单线程的应用程序,”后来,我们的程序性能有问题,所以需要变成多线
- 本文实例讲述了C#自适应合并文件的方法。分享给大家供大家参考。具体实现方法如下:using System;using System.IO;n
- 主要为以下实现步骤:1.绑定域名先登录微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”。(特别提示不需要加上http或
- 实现窗口小部件,访问手机储存卡指定目录中的图片文件,然后随机选择一张在窗口的小部件中显示。图片路径使用List存储,适合初级Android学