Spring JPA聯(lián)表查詢之OneToOne源碼詳解
前言
前面幾篇我們學(xué)習(xí)的都是單表查詢,就是對(duì)一張表中的數(shù)據(jù)進(jìn)行查詢。而實(shí)際項(xiàng)目中,基本都會(huì)有多張表聯(lián)合查詢的情況,今天我們就來(lái)了解下JPA的聯(lián)表查詢是如做的。
源碼
@OneToOne 注解實(shí)現(xiàn)一對(duì)一關(guān)系映射。比如用戶跟車輛的關(guān)系(這里假設(shè)一個(gè)人只能有一輛車),一個(gè)用戶只能對(duì)應(yīng)一輛車,而一輛車同樣只能對(duì)應(yīng)一個(gè)用戶。
老規(guī)矩,在實(shí)例之前,我們先看看源碼:
public @interface OneToOne {
Class targetEntity() default void.class;
CascadeType[] cascade() default {};
FetchType fetch() default EAGER;
String mappedBy() default "";
boolean orphanRemoval() default false;
}
注解屬性
- targetEntity:(可選)表示默認(rèn)關(guān)聯(lián)的實(shí)體類型,默認(rèn)為當(dāng)前標(biāo)注的實(shí)體類
- cascade:(可選)當(dāng)前類對(duì)象操作后級(jí)聯(lián)對(duì)象的操作。默認(rèn)為不級(jí)聯(lián)任何操作。
- fetch:(可選)關(guān)聯(lián)是否延遲加載或者立刻加載。立刻加載是立刻獲取關(guān)聯(lián)的實(shí)體;延遲加載是表示關(guān)系類在被訪問(wèn)時(shí)才加載。默認(rèn)值EAGER,也就是立刻加載。
- mappedBy:(可選)擁有關(guān)聯(lián)關(guān)系的域,如果關(guān)系是單向的就不需要;如果是雙向關(guān)系表,那么擁有關(guān)系的這一方有建立、解除和更新與另一方關(guān)系的能力,而另一方?jīng)]有,只能被動(dòng)管理,這個(gè)屬性被定義在關(guān)系的被擁有方
- orphanRemoval:(可選)是否將刪除操作應(yīng)用于具有已從關(guān)系中刪除,并將刪除操作級(jí)聯(lián)到這些實(shí)體。
單向聯(lián)表
我這里所說(shuō)的單向聯(lián)表就是只有一方添加注解;通俗講就是我可以通過(guò) user 獲取到其 car 的信息,而不同通過(guò) car 獲取到其 user 的信息。
user 實(shí)體類
@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 實(shí)體類
@Data
@Entity
public class Car {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
}
執(zhí)行請(qǐng)求 /user/findById?id=1,控制臺(tái)打印如下:
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=?
查詢結(jié)果
Optional[User(id=1, name=lili, age=11, car=Car(id=1, name=蘇A00001))]
從上面的 JPQL 語(yǔ)句我們可以發(fā)現(xiàn),uer 表 和 car 表 是通過(guò)left outer jion進(jìn)行連接的。這樣我們可以通過(guò)查詢 user 信息來(lái)獲取其使用的 car 信息。
雙向聯(lián)表
我們除了需要通過(guò) user 信息來(lái)獲取其 car 信息為,有時(shí)還需要通過(guò) car 信息來(lái)獲取其 user 信息。只要再在 car 的實(shí)體中添加 user 字段,并添加@OneToOne注解。(這里有一個(gè)堆棧溢出的問(wèn)題需要注意一下,詳情請(qǐng)見(jiàn) JPA 錯(cuò)題集 第二個(gè)報(bào)錯(cuò)信息)
user 實(shí)體
@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 實(shí)體
@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;
}
執(zhí)行請(qǐng)求 /car/findById?id=1,控制臺(tái)打印如下:
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=?
查詢結(jié)果
Optional[Car(id=1, name=蘇A00001, user=com.example.sbjdemo.pojo.User@1c0775ea)] 用戶信息可以通過(guò)后面的實(shí)體類獲取
延遲加載(懶加載)
上面是 OneToOne 的情形,所以請(qǐng)求出來(lái)的數(shù)據(jù)也是比較有限的。試想一下,如果是 OneToMany,而我們只想獲取 one 的信息,但是 many 的數(shù)據(jù)卻跟著一起請(qǐng)求出來(lái)的,無(wú)論是從數(shù)據(jù)上還是性能上來(lái)講,都是一種負(fù)擔(dān)。那么,有沒(méi)有一種方法,讓我們能夠只獲取 one 的信息,而對(duì)于 many 可以動(dòng)態(tài)獲取呢?這時(shí),我們想到了源碼中一個(gè) fetch 方法,他好像可以使用 LAZY 屬性來(lái)控制數(shù)據(jù)懶加載。廢話少說(shuō),上碼。
user 實(shí)體
User 實(shí)體類上的 Car 屬性中 @OneToOne 添加屬性 fetch = FetchType.LAZY
@OneToOne(fetch = FetchType.LAZY) @JoinColumn(name = "car_id") @JsonIgnore private Car car;
執(zhí)行請(qǐng)求 /user/findById?id=1,控制臺(tái)打印如下:
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=?
查詢結(jié)果:
Optional[com.example.sbjdemo.pojo.User@30c311e6] 通過(guò)控制臺(tái)可以看出,只有一個(gè)用戶查詢的 sql,結(jié)果也就是一個(gè) user 的實(shí)體類數(shù)據(jù)。那我們的 car 信息該如何查看呢?其實(shí)我們可以使用查詢結(jié)果 result 去 get 我們所需要的數(shù)據(jù),如下圖所示:

查詢完會(huì)發(fā)現(xiàn),控制臺(tái)又打印了一個(gè) 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=?
恍然大悟,原來(lái)還是通過(guò) sql 去完成的。
最后結(jié)論
oneToOne 主要針對(duì)一對(duì)一的場(chǎng)景,他們雙方都可以作為維護(hù)端或者被維護(hù)端,所以 mappedBy 這個(gè)屬性可有可無(wú)。這也要是與其他三個(gè)映射關(guān)系不同的地方。
以上就是Spring JPA聯(lián)表查詢之OneToOne源碼詳解的詳細(xì)內(nèi)容,更多關(guān)于Spring JPA聯(lián)表查詢的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SpringBoot項(xiàng)目部署時(shí)application.yml文件的加載優(yōu)先級(jí)和啟動(dòng)腳本問(wèn)題
Spring Boot在啟動(dòng)時(shí)會(huì)根據(jù)一定的優(yōu)先級(jí)順序加載配置文件,優(yōu)先級(jí)從高到低依次是:命令行參數(shù)、Jar包外部config目錄下的配置文件、Jar包同級(jí)目錄下的配置文件、classpath下的/config目錄、classpath根路徑2024-09-09
解析MyBatis源碼實(shí)現(xiàn)自定義持久層框架
這篇文章主要介紹了手撕MyBatis源碼實(shí)現(xiàn)自定義持久層框架,涉及到的設(shè)計(jì)模式有Builder構(gòu)建者模式、??模式、代理模式,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-05-05
淺談MyBatis通用Mapper實(shí)現(xiàn)原理
這篇文章主要介紹了淺談MyBatis通用Mapper實(shí)現(xiàn)原理,本文會(huì)先介紹通用 Mapper 的簡(jiǎn)單原理,然后使用最簡(jiǎn)單的代碼來(lái)實(shí)現(xiàn)這個(gè)過(guò)程。感興趣的小伙伴們可以參考一下2018-10-10
java開(kāi)發(fā)https請(qǐng)求ssl不受信任問(wèn)題解決方法
這篇文章主要介紹了java開(kāi)發(fā)https請(qǐng)求ssl不受信任問(wèn)題解決方法,具有一定借鑒價(jià)值,需要的朋友可以參考下2018-01-01

