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.PERSISTCascadeType.REMOVECascadeType.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,如下图所示:

Spring JPA联表查询之注解属性详解


而立刻加载的时候(EAGER)会将主体数据和所有级联数据请求出来,对于大数据的情况,系统可能会存在一定的压力,所以实际项目中,我们大多数情况下会使用 FetchType.LAZY

四、mappedBy

(可选)拥有关联关系的域,如果关系是单向的就不需要;如果是双向关系表,那么拥有关系的这一方有建立、解除和更新与另一方关系的能力,而另一方没有,只能被动管理,这个属性被定义在关系的被拥有方

五、orphanRemoval

(可选)是否将删除操作应用于具有已从关系中删除,并将删除操作级联到这些实体;默认为false。

来源:https://juejin.cn/post/7226303381261221946

标签:Spring,JPA,联表查询,注解属性
0
投稿

猜你喜欢

  • 在springboot中如何使用filter设置要排除的URL

    2022-11-06 00:06:01
  • 深入解析C#编程中struct所定义的结构

    2022-03-01 12:43:08
  • Spring Boot整合Mybatis并完成CRUD操作的实现示例

    2023-11-09 04:36:46
  • Android非XML形式动态生成、调用页面的方法

    2022-11-11 11:26:01
  • Java面向对象基础,类,变量,方法

    2023-04-08 13:21:49
  • MyBatis中使用$和#所遇到的问题及解决办法

    2023-03-25 12:27:55
  • Java的SPI机制实例详解

    2021-08-15 08:47:41
  • SpringMVC执行过程详细讲解

    2023-06-07 10:04:54
  • springboot实现公众号接收回复消息和超过5秒被动回复消息

    2022-10-16 04:40:48
  • SpringMVC RESTFul实战案例访问首页

    2022-03-12 00:21:01
  • spring cglib 与 jdk 动态代理

    2021-07-19 20:28:43
  • Python代码一键转Jar包及Java调用Python新姿势

    2023-02-13 08:16:22
  • Java Exception 捕获和显示实例详解

    2022-07-05 22:41:09
  • Springboot中如何使用Redisson实现分布式锁浅析

    2023-10-01 18:48:29
  • Spring Bean创建流程分析讲解

    2022-04-20 22:53:07
  • Android中RecyclerView 滑动时图片加载的优化

    2021-07-29 09:58:12
  • Spring Cloud下OAUTH2注销的实现示例

    2023-06-01 22:54:58
  • 详解Java的Hibernat框架中的Map映射与SortedMap映射

    2021-08-21 20:31:59
  • C#操作串口通信协议Modbus的常用方法介绍

    2023-01-20 04:06:58
  • java 中HttpClient传输xml字符串实例详解

    2023-11-24 13:40:54
  • asp之家 软件编程 m.aspxhome.com