說說如何使用 Spring Data JPA 持久化數(shù)據(jù)

1 JPA

JPA全稱為Java Persistence API(Java持久層API),它是在 jdk 5中提出的Java持久化規(guī)范。它為開發(fā)人員提供了一種對象/關(guān)聯(lián)映射工具,實現(xiàn)管理應(yīng)用中的關(guān)系數(shù)據(jù),從而簡化Java對象的持久化工作。很多ORM框架都是實現(xiàn)了JPA的規(guī)范,比如:Hibernate、EclipseLink 等。

1.1 Java 持久層框架

Java 持久層框架訪問數(shù)據(jù)庫的方式分為兩種。一種以 SQL 為核心,封裝一定程度的 JDBC 操作,比如: MyBatis 框架。另一種是以 Java 實體類為核心,建立實體類和數(shù)據(jù)庫表之間的映射關(guān)系,也就是ORM框架,比如:Hibernate、Spring Data JPA。

1.2 JPA 規(guī)范

  1. ORM映射元數(shù)據(jù):JPA支持XML和注解兩種元數(shù)據(jù)形式。元數(shù)據(jù)用于描述對象和表之間的映射關(guān)系,框架會據(jù)此將實體對象持久化到數(shù)據(jù)庫表中。

  2. JPA 的API:用來操作實體對象,執(zhí)行CRUD操作。對于簡單的 CRUD 操作,開發(fā)人員可以不用寫代碼。

  3. JPQL查詢語言:以面向?qū)ο蟮姆绞絹聿樵償?shù)據(jù)。

1.3 Hibernate

Hibernate 框架可以將應(yīng)用中的數(shù)據(jù)模型對象映射到關(guān)系數(shù)據(jù)庫表的技術(shù)。

JPA 是規(guī)范,而Hibernate是JPA的一種實現(xiàn)框架。

2 Spring Data JPA

Spring Data JPA 在實現(xiàn)了JPA規(guī)范的基礎(chǔ)上封裝的一套 JPA 應(yīng)用框架。使用Spring Data JPA能夠在不同的ORM框架之間方便地進(jìn)行切換而不需要更改代碼。Spring Data JPA 的目標(biāo)是統(tǒng)一ORM框架的訪問持久層操作,來提高開發(fā)效率。

Spring Data JPA只是一個抽象層,主要用于減少為各種持久層存儲實現(xiàn)數(shù)據(jù)訪問層所需的樣板代碼量。它的 JPA 實現(xiàn)層就是采用 Hibernate 框架實現(xiàn)的。

2.1 引入依賴包

在 Spring Boot 應(yīng)用中,只需要打開 pom.xml 加入一個 Spring Data JPA 依賴即可。 這個依賴不僅會引入 Spring Data JPA ,還會傳遞性地將 Hibernate 作為 JPA 實現(xiàn)引入進(jìn)來。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

2.2 實體類注解

(1)@Entity

類注解,用于標(biāo)識這個實體類是一個JPA實體。

(2)@Table(name = "自定義表名")

類注解,用于自定義實體類在數(shù)據(jù)庫中所對應(yīng)的表名,默認(rèn)是實體類名。特別是那些被作為數(shù)據(jù)庫關(guān)鍵字的實體類名,就會用到這個注解來指定表名。

(3)@Id

類變量注解,用于指定主鍵。

(4)@GeneratedValue

類變量注解,用于指定主鍵的生成策略。

它包含strategy屬性,具體說明如下:

strategy 說明
AUTO 由程序控制,默認(rèn)選項
IDENTITY 由數(shù)據(jù)庫自增長模式生成,適用于 MySQL
SEQUENCE 由數(shù)據(jù)庫序列生成,適用于 Oracle
Table 由指定的表生成

(5)@Basic

指定類變量讀取方法到數(shù)據(jù)庫表字段的映射關(guān)系。對于沒有任何特殊注解的getXxxx()方法,默認(rèn)帶有 @Basic 注解。也就是說,除非特殊情況,否則所有的類變量都帶有 @Basic 注解,這些變量都映射到指定的表字段中。

@Basic 注解有一個 fetch 屬性用于表示讀取策略。策略有兩種EAGER和LAZY,它們分別表示為主動讀取與懶加載。默認(rèn)為 EAGER。

(6)@Column

表示列的說明,如果字段名與列名相同,則可以省略。

@Column 注解擁有以下屬性:

屬性 說明
name 在數(shù)據(jù)庫表中所對應(yīng)字段的名稱。
length 字段的長度。當(dāng)字段的類型為varchar時,才有效;默認(rèn)為255個字符。
nullable 是否可以為null值,默認(rèn)是true。
precision和scale 表示精度,當(dāng)字段類型為double時,precision表示數(shù)值的總長度,scale表示小數(shù)點所占的位數(shù)。

(7)@Transient

類變量注解,表示該變量不是一個到數(shù)據(jù)庫表的字段映射。因為類變量的默認(rèn)注解是 @Basic,所以某些場景下的非持久化類變量就會用到該注解。

(8)@Temporal

類變量注解(也可用在 getXxx 方法上),表示時間格式。具體說明如下:

語法 說明
@Temporal(TemporalType.DATE) 日期,形如 2020-10-10
@Temporal(TemporalType.TIME) 時間,形如 10:10:10
@Temporal(TemporalType.TIMESTAMP) 默認(rèn)值;日期+時間,形如 2020-10-10 10:10:10

Craig Walls 舉了這樣一個實體類代碼示例:

@Data
@RequiredArgsConstructor
@NoArgsConstructor(access = AccessLevel.PRIVATE, force = true)
@Entity
public class Ingredient {
    @Id
    private final String id;
    private final String name;
    private final Type type;

    public static enum Type {
        WRAP, PROTEIN, VEGGIES, CHEESE, SAUCE
    }
}

示例中除了應(yīng)用 @Entity 與 @Id 注解之外,還在類級別添加了 @NoArgsConstructor 注解 。因為 JPA 需要實體類提供一個無參構(gòu)造器,所以這里利用 Lombok 的 @NoArgsConstructor 注解來生成這個構(gòu)造器。

@NoArgsConstructor 注解還可以將這個無參構(gòu)造器私有化(access = AccessLevel.PRIVATE),這樣外部就不能直接調(diào)用。

因為這個類的變量 id、name 與 type 還未初始化,所以我們還需要把 force 設(shè)置為 true,將其初始化為 null。

雖然 @Data 注解會為我們添加一個有參構(gòu)造器,但因為之前添加了 @NoArgsConstructor 注解,所以有參構(gòu)造器就沒了。因此,必須再添加一個 @RequiredArgsConstructor 注解,強(qiáng)制生成一個有參構(gòu)造器。

2.3 實體類關(guān)系注解

Spring Data JPA 有四種關(guān)系注解,它們分別是 @OneToOne、@OneToMany、@ManyToOne 和@ManyToMany。

這四種關(guān)系注解都有 fetch 與 cascade 兩種屬性。

fetch 屬性用于指定數(shù)據(jù)延遲加載策略:

策略 說明
FetchType.LAZY 默認(rèn)值;懶加載
FetchType.EAGER 立即加載

cascade 屬性用于指定級聯(lián)策略:

策略 說明
CascadeType.PERSIST 級聯(lián)持久化;保存父實體時,也會同時保存子實體。
CascadeType.MERGE 級聯(lián)合并;修改了子實體,保存父實體時也會同時保存子實體(常用)。
CascadeType.REMOVE 級聯(lián)刪除;刪除父實體時,會級聯(lián)刪除關(guān)聯(lián)的子實體。
CascadeType.REFRESH 級聯(lián)刷新;獲取父實體的同時也會重新獲取最新的子實體。
CascadeType.ALL 以上四種策略
默認(rèn)值

因為這四種注解只能表示實體之間幾對幾的關(guān)系,指定與所操作實體相關(guān)聯(lián)的數(shù)據(jù)庫表中的列字段,就需要用到 @JoinColumn 注解。

假設(shè)有這樣的一組實體關(guān)系。一個用戶擁有一個密碼;而一個用戶屬于一個部門,一個部門下?lián)碛卸鄠€用戶;一個用戶可以擁有多個角色,而一個角色下也可以包含多個用戶。

(1)@OneToOne

@OneToOne 用來表示一對一的關(guān)系,放置在主導(dǎo)類上。比如用戶類會有一個指定密碼表的主鍵 pwd_id,將 @OneToOne 放置在用戶類的 pwd 字段上,就可以表示用戶類與密碼類是一對一的關(guān)系,并且主導(dǎo)類是用戶類。

@OneToOne
@JoinColumn(name = "pwd_id")
private Password pwd;

也可以不使用 @JoinColumn,Hibernate 會自動在用戶表生成關(guān)聯(lián)字段,字段默認(rèn)的命名規(guī)則為 “附屬類名_附屬主鍵”,如:password_id。

有時候會看到注解 @PrimaryKeyJoinColumn(name = "...") ,其實它本質(zhì)上是 @Id 與 @JoinColumn(name = "...") 的組合體。

(2)@OneToMany

在分析用戶與部門之間關(guān)系時,會發(fā)現(xiàn)一個用戶只能屬于一個部門,而一個部門可以包含有多個用戶。所以,如果站在部門的角度來看

在分析用戶與部門之間的關(guān)系時,一個員工只能屬于一個部門,但是一個部門可以包含有多個員工,如果我們站在部門的角度來看,部門與員工之間就是一對多的關(guān)系,在部門實體類 Department 上添加如下注解:

1.  @OneToMany
2.  @JoinColumn(name = "department_id")
3.  private List<User> user;

如果不指定@JoinColumn 注解,Hibernate會自動生成一張中間表來對用戶和部門進(jìn)行綁定,這張中間表默認(rèn)的命名規(guī)則為:實體類表名_實體類中指定的屬性名。例如,部門表名為 t_department ,部門實體類中關(guān)聯(lián)的用戶集合屬性名為 user,則默認(rèn)生成的中間表名為:t_department_user。
在實踐中,我們推薦使用@JoinTable注解來直接指定中間表:

@OneToMany
@JoinTable(name = " t_department_user ", joinColumns = {
@JoinColumn(name = "department_id") }, inverseJoinColumns = { @JoinColumn(name = "user_id") })
private List<User> users;

@JoinColumn 中的 name 屬性用于指定當(dāng)前實體類(部門)所對應(yīng)表的關(guān)聯(lián) ID;inverseJoinColumns 屬性用于指定所關(guān)聯(lián)的實體類表(員工)的關(guān)聯(lián) ID,里面內(nèi)嵌了 @JoinColumn 注解。

(3)@ManyToOne(多對一)

如果我們站在用戶的角度來看待用戶與部門之間的關(guān)系時,它們之間就變成了多對一的關(guān)系(多個用戶隸屬于一個部門),在用戶實體類 User 上添加如下注解:

@ManyToOne
@JoinColumn(name = "department_id")
private Department department;

(4)@ManyToMany(多對多)

用戶與角色之間是多對多的關(guān)系,因為一個用戶可以擁有多個角色,而一個角色也可以隸屬于多個員工。多對多關(guān)系一般通過創(chuàng)建中間表來進(jìn)行關(guān)聯(lián),這時就會用到 @JoinTable注解。

在用戶實體類中添加如下注解:

@ManyToMany
@JoinTable(name = "t_user_role", joinColumns = { @JoinColumn(name = "user_id") }, inverseJoinColumns = {
@JoinColumn(name = "role_id") })
private List<Role> roles;

在角色實體類中添加如下注解:

@ManyToMany
@JoinTable(name = "t_user_role", joinColumns = { @JoinColumn(name = "role_id") }, inverseJoinColumns = {
@JoinColumn(name = "user_id") })
private List<User> users;
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容