Spring JPA联表查询之OneToMany源码解析
作者:烟雨戏江南 发布时间:2023-12-25 06:10:47
前言
我们在实际项目中,除了会碰到一对一的情况,还有一对多的情况,比如一个用户可以有多辆车,而一辆车只能有一个用户等等,今天我们就来一起学习下 OneToMany(一对多)。
源码
@OneToMany 注解实现一对多关系映射。比如用户跟房子的关系, 一个用户可以有好多房子,而一个房子只能一个用户。
老规矩,实例之前先看看源码:
public @interface OneToMany {
Class targetEntity() default void.class;
CascadeType[] cascade() default {};
FetchType fetch() default LAZY;
String mappedBy() default "";
boolean orphanRemoval() default false;
}
注解属性详情请见 注解属性详解,其中需要注意的是 @OneToMany 的 fetch 的默认值是 LAZY。
单向联表
我这里所说的单向联表就是只有一方添加注解;通俗讲就是我可以通过 user 获取到其 house 的信息,而不同通过 house 获取到其 user 的信息。
user 实体类
因为对方是 many 多端,所以这边需要用 List 集合
@Entity
@Data
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
private int age;
@OneToMany
@JoinColumn(name = "user_id")
private List<House> house;
}
house 实体类
@Entity
@Data
public class House {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String addr;
}
小贴士
实例运行之后,大家肯定会发现一个奇怪的问题:user_id 字段明明写在了 user 实体类中,但是实际却添加在 house 表中,这是为啥呢?
因为在 JPA 规范中,一对多的双向关系是由 多端(many) 来维护。就是说 多端(many) 为关系维护端,负责关系的增删改查;一端(one) 则为关系被维护端。
执行请求/user/findById?id=1
,控制台打印如下:
Hibernate:
select
user0_.id as id1_2_0_,
user0_.age as age2_2_0_,
user0_.name as name3_2_0_
from
user user0_
where
user0_.id=?
Hibernate:
select
house0_.user_id as user_id3_1_0_,
house0_.id as id1_1_0_,
house0_.id as id1_1_1_,
house0_.addr as addr2_1_1_
from
house house0_
where
house0_.user_id=?
查询结果
User(id=1, name=lili, age=11, house=[House(id=1, addr=江苏南京), House(id=2, addr=江苏无锡), House(id=3, addr=江苏苏州)])
双向联表
我们除了需要通过 user 信息来获取其 house 信息外,有时还需要通过 house 信息来获取其 user 信息。但是需要注意的是 user 对于 house 来说,是一对多;而 house 对于 user 来说,是多对一。这时我们就需要引入另一个联表注解 @ManyToOne
,在 house 的实体中添加 user 字段,并添加 @ManyToOne
注解。(这里有一个堆栈溢出的问题需要注意一下,详情请见 JPA 错题集 第二个报错信息)
user 实体类
user 实体中 house 字段
@OneToMany
@JoinColumn(name = "user_id")
@JsonIgnore
public List<House> house;
house 实体类
@ManyToOne
@JsonIgnore
private User user;
执行请求 /house/findById?id=1
,控制台打印如下:
Hibernate:
select
house0_.id as id1_1_0_,
house0_.addr as addr2_1_0_,
house0_.user_id as user_id3_1_0_,
user1_.id as id1_2_1_,
user1_.age as age2_2_1_,
user1_.name as name3_2_1_
from
house house0_
left outer join
user user1_
on house0_.user_id=user1_.id
where
house0_.id=?
2023-04-27 20:35:29.940 TRACE 24272 --- [nio-7777-exec-6] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [INTEGER] - [1]
对象获取
有同学可能已经发现了,当添加了 @JsonIgnore
这个属性之后
执行请求 /user/findById?id=1
,控制台打印如下:
Hibernate:
select
user0_.id as id1_2_0_,
user0_.age as age2_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]
只取进行用户信息的查询,那我的 house 怎么办呢?别急,我会给你们找回来的!
User 的 control 层
@GetMapping("findById")
public Optional<User> findById(int id){
Optional<User> users = userService.findById(id);
users.get().getHouse().forEach(v->{
System.out.println(v.getId() + "-"+ v.getAddr());
});
return userService.findById(id);
}
执行请求 /user/findById?id=1
,控制台打印如下:
Hibernate:
select
house0_.user_id as user_id3_1_0_,
house0_.id as id1_1_0_,
house0_.id as id1_1_1_,
house0_.addr as addr2_1_1_,
house0_.user_id as user_id3_1_1_
from
house house0_
where
house0_.user_id=?
[nio-7777-exec-1] o.h.type.descriptor.sql.BasicBinder : binding parameter [1] as [INTEGER] - [1]
1-南京
2-苏州
3-无锡
来源:https://juejin.cn/post/7226665757882564666
猜你喜欢
- 如何更改 C# Record 构造函数的行为Record[1] 是 C# 9 中的一个新功能。Record是从Structs[2]借用的特殊
- 博主第一次安装Android Studio 3.6版本的时候就找不到R.java文件,于是在网上找个各种方法,但是都没能解决问题。注意:本博
- 这篇文章memo一下Jvm中关于时区设定的基础操作。Java的时区设定这里列出如下三种方式方式说明TimeZone.setDefault方式
- 通过下面代码在构造函数中调用方法 SetShadow();即可实现无边框窗体的阴影效果了需要添加命名空间 using System.Runt
- IEnumerable这个接口在MSDN上是这么说的,它是一个公开枚举数,该枚举数支持在非泛型集合上进行简单的迭代。换句话说,对于所有数组的
- 项目中肯定会遇到需要用户自己绘制地形的需求,然后根据地形自动生成房间。下面说说我在绘制地形的实现方法。我们百度可以看到很多关于自己创建mes
- 后台控制层: public static final String HEAD_IMG_DIR = "D:/upload/&quo
- activity动画方式在AndroidMenifest中添加activity的动画属性windowAnimationStyle <i
- Springboot内部提供的事务管理器是根据autoconfigure来进行决定的。比如当使用jpa的时候,也就是pom中加入了sprin
- 本文实例讲述了Android编程之DatePicker和TimePicke简单时间监听用法。分享给大家供大家参考,具体如下:DatePick
- Android当道,现在学习Android开发还晚吗?写下这个问题的时间是–2014年6月15号,我会回答:不晚,Android至少还能在活
- 在实现下拉框的基础上进行二级联动(这个项目有bug添加可以完成,但是修改获取不到对应的值,这个问题解决以后我会在发布一篇文章)JS部分//二
- JavaMail API中定义了一个java.mail.Transport类,它专门用于执行邮件发送任务,这个类的实例对象封装了某种邮件发送
- 用Visual Studio等IDE写C#的Hello World非常简单,但脱离了IDE你能不能打印出Hello World呢?这不是说工
- Gateway什么是Gateway  由于Netflix的zuul发生问题,spring公司自己研发了一
- 这篇文章主要介绍了Java List分页功能实现代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的
- 第一步:在d盘新建android文件夹,在此文件夹中再建三个子文件夹,Android Studio 、 Android_SDK、Androi
- char 字符char代表一个Unicode字符,它是System.Char的别名char someChar = 'a';/
- 一、MyBatis的逆向⼯程(1)所谓的逆向⼯程是:根据数据库表逆向⽣成Java的pojo类,SqlMapper.xml⽂件,以及Mappe
- 背景在我们日常开发中,多线程管理一直是非常头疼的问题之一,尤其在历史性长,结构复杂的app中,线程数会达到好几百个甚至更多,然而过多的线程不