最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

簡潔持久層開發(fā)—Spring Data JPA

2023-07-03 16:00 作者:啥玩意你再說一遍  | 我要投稿

1前言

Spring項目開發(fā)中,持久層的框架常用的有MyBatis、Mybatis Plus、Hibernate和Spring Data JPA等,國內(nèi)常用的是前兩種,本人最先接觸的也是MyBatis。下面我列舉了常用的三類框架的一些對比信息。今天我們要聊一聊一種解放雙手的持久成框架Spring Data JPA。

Mybatis:https://mybatis.org/mybatis-3/zh/java-api.html

Mybatis Plus:https://github.com/baomidou/mybatis Plus

Spring Data JPA:https://spring.io/projects/spring-data-jpa

Mybatis

Mybatis 是一個優(yōu)秀的持久層框架,它支持定制化 SQL、存儲過程以及高級映射。Mybatis 避免了幾乎所有的 JDBC 代碼和手動設置參數(shù)以及獲取結(jié)果集。Mybatis 可以使用簡單的 XML 或注解來配置和映射原生類型、接口和 Java 的 POJO(Plain Old Java Objects,普通的 Java 對象)為數(shù)據(jù)庫中的記錄。

Mybatis Plus

MybatisPlus 是一款 Mybatis 的增強工具,在 Mybatis 的基礎(chǔ)上只做增強,不做改變,為簡化開發(fā)、提高效率而生。Mybatis Plus 為 Mybatis 提供了一些額外的功能,如:通用 Mapper、通用 Service、分頁插件、性能分析插件等。這些功能簡化了 CRUD 操作,減少了重復代碼的編寫,提高了開發(fā)效率。

Spring Data JPA

Spring Data JPA 是一個基于 Spring Data 和 JPA(Java Persistence API,Java 持久化 API)技術(shù)的持久層框架,它使得開發(fā)者可以更加輕松地實現(xiàn)對數(shù)據(jù)的訪問和操作。Spring Data JPA 提供了一套簡化 JPA 規(guī)范的抽象接口,通過擴展這些接口,可以實現(xiàn)對數(shù)據(jù)的 CRUD(增刪查改)操作。此外,Spring Data JPA 還支持自定義查詢、分頁查詢、排序等功能。

共同點

  1. 持久層框架:Mybatis、Mybatis Plus 和 Spring Data JPA 都是為了簡化和優(yōu)化數(shù)據(jù)庫訪問操作的持久層框架。

  2. 減少代碼量:三者都旨在減少開發(fā)者編寫 JDBC 代碼的工作量,簡化數(shù)據(jù)庫訪問操作。

  3. 易于整合:三個框架都可以輕松地與 Spring 框架進行整合,實現(xiàn)依賴注入和事務管理等功能。

  4. 支持 POJO:它們都支持將數(shù)據(jù)庫的表映射為 Java 對象,使得開發(fā)者可以更直觀地處理數(shù)據(jù)。

  5. 抽象接口:Mybatis、Mybatis Plus 和 Spring Data JPA 都提供了抽象接口,以簡化對數(shù)據(jù)的 CRUD 操作。

不同點

  1. 定位和發(fā)展歷史:

    • MyBatis是一個持久層框架,旨在提供對關(guān)系型數(shù)據(jù)庫的直接訪問,通過XML或注解配置SQL語句和映射。

    • MyBatis Plus是MyBatis的增強版本,提供了更多的便利功能和擴展,如代碼生成器、通用Mapper、分頁插件等。

    • Spring Data JPA是基于JPA(Java Persistence API)規(guī)范的持久層框架,與關(guān)系型數(shù)據(jù)庫和ORM(對象關(guān)系映射)框架無關(guān),它提供了一種更高級的抽象和簡化的方式來操作數(shù)據(jù)庫。

  2. 編程模型:

    • MyBatis和MyBatis Plus使用XML或注解配置SQL語句和映射關(guān)系,提供了靈活的SQL編寫方式,對SQL的精細控制力度較高。

    • Spring Data JPA使用繼承和命名約定來自動生成實體類與數(shù)據(jù)庫表之間的映射,并支持通過方法命名規(guī)則自動生成常用的增刪改查操作。

  3. 對象關(guān)系映射(ORM)支持:

    • MyBatis和MyBatis Plus提供了基本的ORM功能,但需要手動編寫SQL語句和映射關(guān)系。

    • Spring Data JPA是一個全功能的ORM框架,通過實體類和注解自動完成SQL查詢和結(jié)果映射,減少了開發(fā)人員編寫和維護SQL的工作量。

  4. 領(lǐng)域模型支持:

    • MyBatis和MyBatis Plus更加偏向于面向SQL和數(shù)據(jù)操作的編程模型,不涉及復雜的領(lǐng)域模型定義。

    • Spring Data JPA更加偏向于領(lǐng)域模型的設計,支持實體之間的關(guān)系映射、繼承關(guān)系等高級語義。

  5. 社區(qū)和生態(tài)系統(tǒng):

    • MyBatis和MyBatis Plus擁有龐大的用戶社區(qū)和豐富的生態(tài)系統(tǒng),提供了許多插件和擴展,具有豐富的第三方支持。

    • Spring Data JPA是Spring框架的一部分,與Spring集成緊密,與其他Spring組件無縫協(xié)作,享受Spring強大的功能和社區(qū)支持。

優(yōu)缺點

MyBatis: 優(yōu)點:

  1. 靈活性:MyBatis允許開發(fā)者編寫原生SQL語句,具有更高的靈活性,并可以更好地優(yōu)化SQL語句性能。

  2. 擴展性:MyBatis提供了許多插件和擴展機制,可以根據(jù)項目的需要進行定制和擴展。

  3. 易于學習和使用:MyBatis的學習曲線相對較低,配置簡單,上手快。

  4. 輕量級:相比于其他ORM框架,MyBatis的依賴較少,對系統(tǒng)資源的消耗較小。

缺點:

  1. 編寫大量的SQL語句:相比于ORM框架,MyBatis需要開發(fā)者手動編寫和維護大量的SQL語句,對于復雜查詢和關(guān)聯(lián)查詢需要更多的工作量。

  2. 開發(fā)效率較低:MyBatis需要編寫大量的XML配置文件,增加了開發(fā)的工作量。

MyBatis Plus: 優(yōu)點:

  1. 提供更多的便利功能:MyBatis Plus在MyBatis的基礎(chǔ)上進行增強,提供了更多便利的功能,如代碼生成器、通用Mapper、分頁插件等。

  2. 減少重復性的開發(fā):MyBatis Plus提供了常用的增刪改查操作的封裝,減少了重復性的開發(fā)工作。

缺點:

  1. 學習成本相對較高:相比于MyBatis,MyBatis Plus提供了更多的功能和特性,學習成本可能會略高。

  2. 過度封裝的問題:MyBatis Plus的部分功能可能過度封裝,不夠靈活,無法滿足一些復雜的需求。

Spring Data JPA: 優(yōu)點:

  1. 提高開發(fā)效率:Spring Data JPA提供了一種更高級的抽象和簡化的方式來操作數(shù)據(jù)庫,可以通過方法命名約定自動生成常用的增刪改查操作,減少了開發(fā)人員編寫和維護SQL的工作量。

  2. 領(lǐng)域模型的支持:Spring Data JPA支持實體之間的關(guān)系映射、繼承關(guān)系等高級語義,更適合于復雜業(yè)務需求和領(lǐng)域模型的開發(fā)。

缺點:

  1. 性能可能較低:相對于原生的SQL編寫和優(yōu)化,Spring Data JPA可能存在一些性能瓶頸,尤其是對于復雜查詢和關(guān)聯(lián)查詢。

  2. 不夠靈活:Spring Data JPA提供的抽象層可能不夠靈活,對于一些特殊需求,需要使用原生的SQL語句進行操作。

本人在項目應用中最先接觸的持久層框架是Mybatis,其支持xml文件并提供多種動態(tài)標簽來寫SQL語句,實現(xiàn)了靈活的數(shù)據(jù)處理方式。MyBatis采用#{}預編譯來取代${}的傳參方式,防止SQL注入危險(面試題)。Mybatis支持一級緩存、二級緩存。后來接觸Mybatis Plus與Spring Data JPA,兩者在使用上有些許類似。綜上述說,如果你的項目業(yè)務不是很復雜,持久層框架推薦采用Spring Data JPA,它會讓你的開發(fā)更加簡單,代碼更加簡潔。

2Spring Boot集成

用啥研究啥(σ???)σ..:*☆哎喲不錯哦

2.1依賴

Spring Data JPA的依賴可以通過使用Spring Data Release Train BOM(Bill of Materials)或聲明對Spring Data模塊的依賴這兩種方式來引入。

  1. 使用Spring Data Release Train BOM:

    • 這種方式是通過引入Spring Data Release Train BOM來管理Spring Data相關(guān)依賴的版本。BOM是一個包含了各個模塊的版本信息的POM文件。

    • 通過在項目的pom.xml文件中添加對Spring Data Release Train BOM的依賴聲明,可以自動管理Spring Data JPA以及其他Spring Data模塊的版本一致性。

    • 通過BOM來統(tǒng)一管理版本,可以簡化依賴管理的工作,確保不會發(fā)生不兼容或沖突的版本問題。

  2. 聲明對Spring Data模塊的依賴:

    • 這種方式是直接在項目的pom.xml文件中聲明對具體Spring Data模塊的依賴,比如Spring Data JPA。

    • 在這種方式下,需要明確指定所使用的Spring Data模塊的版本號。

    • 這種方式更加靈活,可以精確控制所使用的每個Spring Data模塊的版本以及依賴關(guān)系。

這兩種方式都能夠引入Spring Data JPA的依賴,主要區(qū)別在于版本管理的方式。如果你選擇使用Spring Data Release Train BOM,可以更方便地管理和升級Spring Data相關(guān)模塊的版本,減少版本沖突和不兼容性的問題。而通過聲明對具體Spring Data模塊的依賴,則可以更加精確地控制所使用的每個模塊的版本。

我們采用方式2來依賴Spring Data JPA。我們在通過IDEA創(chuàng)建Spring Boot項目時候,在Dependencies->SQL中勾選上Spring Data JPA,發(fā)現(xiàn)引入的依賴為

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

而官網(wǎng)提供的依賴為

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

spring-boot-starter-data-jpaspring-data-jpa兩者都是用于支持在Spring Boot項目中使用JPA(Java Persistence API)技術(shù)的依賴。它們的主要區(qū)別在于spring-boot-starter-data-jpa是一個Spring Boot的“啟動器”,而spring-data-jpa是一個基本的依賴。下面是兩者的區(qū)別:

  1. spring-boot-starter-data-jpa

    這是一個Spring Boot的啟動器,它包含了一系列用于簡化Spring Boot項目中使用JPA技術(shù)的依賴。啟動器的作用是為了簡化開發(fā)者的配置工作,它會自動地引入所需的庫和配置。

    當你在項目中添加spring-boot-starter-data-jpa依賴時,它會自動包含以下依賴:

    • spring-data-jpa:用于支持Spring Data JPA的基本功能。

    • spring-orm:用于支持Spring的對象關(guān)系映射(Object-Relational Mapping, ORM)。

    • hibernate:作為默認的JPA實現(xiàn)。

    • spring-tx:用于支持事務管理。

    • 其他可能需要的依賴,如數(shù)據(jù)庫連接池等。

  2. spring-data-jpa

    這是一個基本的依賴,提供了Spring Data JPA的核心功能。它包含了對JPA技術(shù)的支持,如實體管理、查詢、事務處理等。如果你只想使用Spring Data JPA的基本功能,而不需要其他的自動配置,可以直接添加這個依賴。

使用spring-boot-starter-data-jpa,因為它可以幫助你簡化配置和管理依賴,這也要求我們在啟動項目時要添加數(shù)據(jù)庫配置信息,我在此采用spring-boot-starter-data-jpa依賴。

2.2配置信息

如果沒有添加數(shù)據(jù)源配置信息,啟動會報一下錯誤,那么Spring Data JPA數(shù)據(jù)源的配置信息在哪里看呢[?_??]

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2023-06-30 17:13:39.327 ERROR 29980 --- [ ? ? ? ? ? main] o.s.b.d.LoggingFailureAnalysisReporter ? :

***************************
APPLICATION FAILED TO START
***************************

Description:

Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.

Reason: Failed to determine a suitable driver class


Action:

Consider the following:
If you want an embedded database (H2, HSQL or Derby), please put it on the classpath.
If you have database settings to be loaded from a particular profile you may need to activate it (no profiles are currently active).

Disconnected from the target VM, address: '127.0.0.1:59605', transport: 'socket'

如果你也有上述問題那么說明你對Spring Boot項目掌握還是不夠全面。

讓我們一起讀一下Spring Boot官網(wǎng)文檔吧(https://docs.spring.io/spring-boot/docs/current/reference/html/documentation.html#documentation.data)。

該官網(wǎng)頁面的目錄是不是和IDEA創(chuàng)建Spring Boot項目時,選擇Dependencies時的目錄很相似。Spring Data JPA相關(guān)的信息當然看“6.Data“目錄先查看。點擊“SQL: Configuring a SQL Datastore, Embedded Database support, Connection pools, and more.”,再該頁面可以查看具體的配置內(nèi)容了,后面就請自行查閱吧。再次再補充一點,在application.properties文件中添加配置后,會發(fā)現(xiàn)所有的配置都來自于“package org.springframework.boot.autoconfigure”,該包的依賴為

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>

該依賴是Spring Boot項目中常用的自動配置模塊。它提供了一組默認的配置類,用于根據(jù)項目中的依賴和配置文件的設置,自動配置Spring Boot應用程序的行為。通過這個依賴,您可以快速搭建起一個基本的Spring Boot應用,并根據(jù)需要進行個性化的配置。

2.2.1數(shù)據(jù)源配置

顧名思義不解釋。

spring.datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:3306/db_example
spring.datasource.username=springuser
spring.datasource.password=ThePassword
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

記得,使用哪個數(shù)據(jù)庫,添加對應數(shù)據(jù)庫驅(qū)動依賴。

2.2.2創(chuàng)建和刪除JPA數(shù)據(jù)庫

直接看package org.springframework.boot.autoconfigure.orm.jpa;下的

@ConfigurationProperties(prefix = "spring.jpa")
public class JpaProperties {...}

該類的成員屬性都是配置項。

1. `spring.jpa.database`
? - 指定使用的數(shù)據(jù)庫類型。
2. `spring.jpa.database-platform`
? - 指定使用的數(shù)據(jù)庫方言(如何與特定數(shù)據(jù)庫交互)。
3. `spring.jpa.defer-datasource-initialization`
? - 延遲初始化數(shù)據(jù)源,即在啟動時不立即連接數(shù)據(jù)庫。
4. `spring.jpa.generate-ddl`
? - 是否根據(jù)實體類生成數(shù)據(jù)庫的DDL語句。
5. `spring.jpa.hibernate.ddl-auto`
? - 控制Hibernate在啟動時如何處理數(shù)據(jù)庫的DDL語句。在此配置中設置為create-drop,表示在應用程序啟動時創(chuàng)建表,并在應用程序關(guān)閉時刪除表。
6. `spring.jpa.hibernate.naming.implicit-strategy`
? - 設置Hibernate的隱式命名策略。在此配置中設置為org.hibernate.boot.model.naming.ImplicitNamingStrategyComponentPathImpl。
7. `spring.jpa.hibernate.naming.physical-strategy`
? - 設置Hibernate的物理命名策略。在此配置中設置為org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl。
8. `spring.jpa.hibernate.use-new-id-generator-mappings`
? - 是否啟用新的ID生成器映射。
9. `spring.jpa.mapping-resources`
? - 額外的JPA映射資源文件。
10. `spring.jpa.open-in-view`
? ?- 啟用OpenEntityManagerInView,即在視圖渲染過程中延遲加載關(guān)聯(lián)實體。
11. `spring.jpa.properties.asdf`
? ?- 自定義的JPA屬性。
12. `spring.jpa.show-sql`
? ?- 是否在控制臺上顯示SQL語句。
13. `spring.data.jpa.repositories.bootstrap-mode`
? ?- JPA倉庫的啟動模式。
14. `spring.data.jpa.repositories.enabled`
? ?- 是否啟用JPA倉庫。

至此,可以正常啟動Spring Boot項目了。

2.3CRUD說明

Spring Data存儲庫抽象中的中心接口是repository。此接口主要充當標記接口,用于捕獲要使用的類型。CrudRepository和ListCrudRepository接口為被管理的實體類提供了復雜的CRUD功能。

IDEA雙擊shift,搜索一下該接口,復制出源碼來,刪除多余的注釋:

package org.springframework.data.repository;

import java.util.Optional;

/**
* Interface for generic CRUD operations on a repository for a specific type.
*
* @author Oliver Gierke
* @author Eberhard Wolff
* @author Jens Schauder
*/
@NoRepositoryBean
public interface CrudRepository<T, ID> extends Repository<T, ID> {

/**
?* Saves a given entity. Use the returned instance for further operations as the save operation might have changed the
?* entity instance completely.
?*
?* @param entity must not be {@literal null}.
?* @return the saved entity; will never be {@literal null}.
?* @throws IllegalArgumentException in case the given {@literal entity} is {@literal null}.
?*/
<S extends T> S save(S entity);

/**
?* Saves all given entities.
?*
?* @param entities must not be {@literal null} nor must it contain {@literal null}.
?* @return the saved entities; will never be {@literal null}. The returned {@literal Iterable} will have the same size
?* ? ? ? ? as the {@literal Iterable} passed as an argument.
?* @throws IllegalArgumentException in case the given {@link Iterable entities} or one of its entities is
?* ? ? ? ? ? {@literal null}.
?*/
<S extends T> Iterable<S> saveAll(Iterable<S> entities);

/**
?* Retrieves an entity by its id.
?*
?* @param id must not be {@literal null}.
?* @return the entity with the given id or {@literal Optional#empty()} if none found.
?* @throws IllegalArgumentException if {@literal id} is {@literal null}.
?*/
Optional<T> findById(ID id);

/**
?* Returns whether an entity with the given id exists.
?*
?* @param id must not be {@literal null}.
?* @return {@literal true} if an entity with the given id exists, {@literal false} otherwise.
?* @throws IllegalArgumentException if {@literal id} is {@literal null}.
?*/
boolean existsById(ID id);

/**
?* Returns all instances of the type.
?*
?* @return all entities
?*/
Iterable<T> findAll();

/**
?* Returns all instances of the type {@code T} with the given IDs.
?* <p>
?* If some or all ids are not found, no entities are returned for these IDs.
?* <p>
?* Note that the order of elements in the result is not guaranteed.
?*
?* @param ids must not be {@literal null} nor contain any {@literal null} values.
?* @return guaranteed to be not {@literal null}. The size can be equal or less than the number of given
?* ? ? ? ? {@literal ids}.
?* @throws IllegalArgumentException in case the given {@link Iterable ids} or one of its items is {@literal null}.
?*/
Iterable<T> findAllById(Iterable<ID> ids);

/**
?* Returns the number of entities available.
?*
?* @return the number of entities.
?*/
long count();

/**
?* Deletes the entity with the given id.
?*
?* @param id must not be {@literal null}.
?* @throws IllegalArgumentException in case the given {@literal id} is {@literal null}
?*/
void deleteById(ID id);

/**
?* Deletes a given entity.
?*
?* @param entity must not be {@literal null}.
?* @throws IllegalArgumentException in case the given entity is {@literal null}.
?*/
void delete(T entity);

/**
?* Deletes all instances of the type {@code T} with the given IDs.
?*
?* @param ids must not be {@literal null}. Must not contain {@literal null} elements.
?* @throws IllegalArgumentException in case the given {@literal ids} or one of its elements is {@literal null}.
?* @since 2.5
?*/
void deleteAllById(Iterable<? extends ID> ids);

/**
?* Deletes the given entities.
?*
?* @param entities must not be {@literal null}. Must not contain {@literal null} elements.
?* @throws IllegalArgumentException in case the given {@literal entities} or one of its entities is {@literal null}.
?*/
void deleteAll(Iterable<? extends T> entities);

/**
?* Deletes all entities managed by the repository.
?*/
void deleteAll();
}

在這個接口中聲明的方法通常被稱為CRUD方法。ListCrudRepository提供了等價的方法,但是它們返回List,而CrudRepository方法返回Iterable。

除了CrudRepository之外,還有一個PagingAndSortingRepository抽象,它添加了額外的方法來簡化對實體的分頁訪問:

public interface PagingAndSortingRepository<T, ID> ?{

?Iterable<T> findAll(Sort sort);

?Page<T> findAll(Pageable pageable);
}

標準CRUD功能存儲庫通常具有對底層數(shù)據(jù)存儲的查詢。使用Spring Data,聲明使用這些查詢變成了一個四步的過程:

  1. 聲明一個接口繼承Repository或它的一個子接口,并輸入域類和它應該處理的ID類型,如下面的例子所示:

    interface PersonRepository extends Repository<Person, Long> { … }
  2. 在接口上聲明查詢方法。

    interface PersonRepository extends Repository<Person, Long> {
    ?List<Person> findByLastname(String lastname);
    }
  3. 設置Spring以使用JavaConfig或XML配置為這些接口創(chuàng)建代理實例。

    import org.springframework.data.….repository.config.EnableJpaRepositories;

    @EnableJpaRepositories
    class Config { … }
  4. 注入存儲庫實例并使用它,如下例所示:

    class SomeClient {

    ?private final PersonRepository repository;

    ?SomeClient(PersonRepository repository) {
    ? ?this.repository = repository;
    ?}

    ?void doSomething() {
    ? ?List<Person> persons = repository.findByLastname("Matthews");
    ?}
    }

Y(^o^)Y好了,按照這四部就可以實現(xiàn)CRUD操作了,但是這里有個疑問:域類(domain class )與ID是什么意思?如果你曾經(jīng)使用過Hibernate,你大概就知道了domain概念的含義。

2.3JPA Domain

Hibernate:https://docs.jboss.org/hibernate/orm/current/userguide/html_single/Hibernate_User_Guide.html#domain-model

domain模型來自數(shù)據(jù)建模領(lǐng)域。它是最終描述您正在處理的問題域的模型。有時您還會聽到persistent classes這個術(shù)語。通過Hibernate提供的注解,實現(xiàn)domain class與數(shù)據(jù)庫表的映射,下面舉例了一個簡單表與domain model(class)的映射實現(xiàn)。

  • DDL:

    create table Contact (
    ? ?id integer not null,
    ? ?first varchar(255),
    ? ?last varchar(255),
    ? ?middle varchar(255),
    ? ?notes varchar(255),
    ? ?starred boolean not null,
    ? ?website varchar(255),
    ? ?primary key (id)
    )
  • domain model:

    @Entity(name = "Contact")
    public static class Contact {

    @Id
    private Integer id;

    private Name name;

    private String notes;

    private URL website;

    private boolean starred;

    //Getters and setters are omitted for brevity
    }

    @Embeddable
    public class Name {

    private String firstName;

    private String middleName;

    private String lastName;

    // getters and setters omitted
    }

Y(^o^)Y好了,簡單說明一下概念,想知道更多直接看官網(wǎng)吧。

2.4CRUD實操

  1. 創(chuàng)建Student domain class

    package com.example.springdatajpademo.model.po;

    /**
    * @Author yrz
    * @create 2023/7/1 9:37
    * @Description TODO
    */

    import lombok.AllArgsConstructor;
    import lombok.Getter;
    import lombok.NoArgsConstructor;
    import lombok.Setter;

    import javax.persistence.*;

    @Entity
    @Table(name = "students")
    @Setter
    @Getter
    @AllArgsConstructor
    @NoArgsConstructor
    public class Student {
    ? ?@Id
    ? ?@GeneratedValue(strategy = GenerationType.IDENTITY)
    ? ?private Long id;

    ? ?@Column(name = "name")
    ? ?private String name;

    ? ?@Column(name = "age")
    ? ?private Integer age;

    ? ?@Column(name = "gender")
    ? ?private Integer gender;

    ? ?@Column(name = "class_id")
    ? ?private String classId;

    ? ?@Column(name = "discipline_id")
    ? ?private String disciplineId;
    }

    編寫完domain class后啟動項目,自動創(chuàng)建數(shù)據(jù)庫表。

  2. 聲明接口以及查詢方法

    package com.example.springdatajpademo.dao;


    import com.example.springdatajpademo.model.po.Student;
    import org.springframework.data.repository.CrudRepository;
    /**
    * @Author yrz
    * @create 2023/7/3 9:50
    * @Description
    * 這里我繼承了CrudRepository,它為您提供了CRUD功能的方法。除此之外還可以繼承ListCrudRepository、ReactiveCrudRepository(響應式存儲)、
    * RxJava3CrudRepository(響應式存儲)、CoroutineCrudRepository(Kotlin)、PagingAndSortingRepository、CoroutineSortingRepository,
    * 如果不想擴展Spring Data接口,還可以用@RepositoryDefinition注釋存儲庫接口。
    */

    public interface SpringDataJpaTestDao extends CrudRepository<Student, Long> {

    ? ?/**
    ? ? * 自定義方法
    ? ? * 根據(jù)名稱查詢學生
    ? ? * @param name
    ? ? * @return
    ? ? */
    ? ?Student findByName(String name);

    ? ?/**
    ? ? * 根據(jù)名稱和性別查詢
    ? ? * @param name
    ? ? * @param gender
    ? ? * @return
    ? ? */
    ? ?Student findByNameAndGender(String name, Integer gender);

    ? ?/**
    ? ? * 根據(jù)名稱和性別查詢
    ? ? * 無法解析該方法名稱,程序報錯
    ? ? * @return
    ? ? */
    // ? ?Student findByMingChengAndXingBie(String mingCheng, Integer xingBie);
    // ? ?Student findByMingChengAndXingBie(String name, Integer gender);
    }package com.example.springdatajpademo;

    import com.alibaba.fastjson2.JSON;
    import com.example.springdatajpademo.dao.SpringDataJpaTestDao;
    import com.example.springdatajpademo.model.po.Student;
    import lombok.extern.slf4j.Slf4j;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;

    import java.util.Iterator;
    import java.util.Optional;

    @SpringBootTest
    @Slf4j
    class SpringDataJpaDemoApplicationTests {

    ? ?@Autowired
    ? ?private SpringDataJpaTestDao springDataJpaTestDao;

    ? ?@Test
    ? ?void contextLoads() {

    ? ?}

    ? ?@Test
    ? ?public void testFind(){
    ? ? ? ?// 查全部
    ? ? ? ?Iterable<Student> all = springDataJpaTestDao.findAll();
    ? ? ? ?Iterator<Student> iterator = all.iterator();
    ? ? ? ?while (iterator.hasNext()){
    ? ? ? ? ? ?Student next = iterator.next();
    ? ? ? ? ? ?log.info("findAll:{}", JSON.toJSONString(next));
    ? ? ? ?}
    ? ? ? ?// 根據(jù)ID查詢
    ? ? ? ?Optional<Student> byId = springDataJpaTestDao.findById(1L);
    ? ? ? ?Student student = byId.get();
    ? ? ? ?log.info("findById:{}", JSON.toJSONString(student));
    ? ? ? ?// 根據(jù)名稱查詢
    ? ? ? ?Student byName = springDataJpaTestDao.findByName("李四");
    ? ? ? ?log.info("findByName:{}",JSON.toJSONString(byName));
    ? ? ? ?// 新增
    // ? ? ? ?Student student1 = new Student(5L,"張三",19,0,"1234","3");
    // ? ? ? ?Student save = springDataJpaTestDao.save(student1);
    // ? ? ? ?log.info("save:{}",JSON.toJSONString(save));
    ? ? ? ?// 根據(jù)名稱和性別查詢
    ? ? ? ?Student student2 = springDataJpaTestDao.findByNameAndGender("張三", 0);
    ? ? ? ?log.info("findByNameAndGender:{}",JSON.toJSONString(student2));
    // ? ? ? ?Student student3 = springDataJpaTestDao.findByMingChengAndXingBie("張三", 1);
    // ? ? ? ?log.info("findByMingChengAndXingBie:{}",JSON.toJSONString(student3));
    ? ?}

    }Hibernate: select student0_.id as id1_0_, student0_.age as age2_0_, student0_.class_id as class_id3_0_, student0_.discipline_id as discipli4_0_, student0_.gender as gender5_0_, student0_.name as name6_0_ from students student0_
    2023-07-03 11:00:25.198 ?INFO 31840 --- [ ? ? ? ? ? main] c.e.s.SpringDataJpaDemoApplicationTests ?: findAll:{"age":18,"classId":"321465","disciplineId":"1","gender":1,"id":1,"name":"張三"}
    2023-07-03 11:00:25.198 ?INFO 31840 --- [ ? ? ? ? ? main] c.e.s.SpringDataJpaDemoApplicationTests ?: findAll:{"age":18,"classId":"321546648","disciplineId":"1","gender":1,"id":2,"name":"李四"}
    2023-07-03 11:00:25.198 ?INFO 31840 --- [ ? ? ? ? ? main] c.e.s.SpringDataJpaDemoApplicationTests ?: findAll:{"age":18,"classId":"123","disciplineId":"1","gender":0,"id":3,"name":"王五"}
    2023-07-03 11:00:25.198 ?INFO 31840 --- [ ? ? ? ? ? main] c.e.s.SpringDataJpaDemoApplicationTests ?: findAll:{"age":19,"classId":"1234","disciplineId":"3","gender":0,"id":4,"name":"張三"}
    Hibernate: select student0_.id as id1_0_0_, student0_.age as age2_0_0_, student0_.class_id as class_id3_0_0_, student0_.discipline_id as discipli4_0_0_, student0_.gender as gender5_0_0_, student0_.name as name6_0_0_ from students student0_ where student0_.id=?
    2023-07-03 11:00:25.214 ?INFO 31840 --- [ ? ? ? ? ? main] c.e.s.SpringDataJpaDemoApplicationTests ?: findById:{"age":18,"classId":"321465","disciplineId":"1","gender":1,"id":1,"name":"張三"}
    Hibernate: select student0_.id as id1_0_, student0_.age as age2_0_, student0_.class_id as class_id3_0_, student0_.discipline_id as discipli4_0_, student0_.gender as gender5_0_, student0_.name as name6_0_ from students student0_ where student0_.name=?
    2023-07-03 11:00:25.258 ?INFO 31840 --- [ ? ? ? ? ? main] c.e.s.SpringDataJpaDemoApplicationTests ?: findByName:{"age":18,"classId":"321546648","disciplineId":"1","gender":1,"id":2,"name":"李四"}
    Hibernate: select student0_.id as id1_0_, student0_.age as age2_0_, student0_.class_id as class_id3_0_, student0_.discipline_id as discipli4_0_, student0_.gender as gender5_0_, student0_.name as name6_0_ from students student0_ where student0_.name=? and student0_.gender=?
    2023-07-03 11:00:25.263 ?INFO 31840 --- [ ? ? ? ? ? main] c.e.s.SpringDataJpaDemoApplicationTests ?: findByNameAndGender:{"age":19,"classId":"1234","disciplineId":"3","gender":0,"id":4,"name":"張三"}
    2023-07-03 11:00:25.287 ?INFO 31840 --- [ionShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
    2023-07-03 11:00:25.293 ?INFO 31840 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource ? ? ? : HikariPool-1 - Shutdown initiated...
    Disconnected from the target VM, address: '127.0.0.1:62235', transport: 'socket'
    2023-07-03 11:00:25.309 ?INFO 31840 --- [ionShutdownHook] com.zaxxer.hikari.HikariDataSource ? ? ? : HikariPool-1 - Shutdown completed.

    Spring Data JPA的默認查找策略是CreateIfNotFound,參閱“3.創(chuàng)建配置類編寫代理實例”。通俗的講就是它可以通過在繼承Repository等接口的接口中自定義方法,然后方法名解析出該方法對應的SQL。下面是官網(wǎng)對于解析方法的一些說明,當我們在使用自定義持久層方法時,如果你想解放雙手不手寫SQL,也需要按照下面的說明命名方法。

    4.4.2. Query Creation

    ...

    解析查詢方法名分為主語和謂語。第一部分(find…By, exists…By)定義查詢的主題,第二部分形成謂詞。引子句(主語)可以包含進一步的表達。find(或其他引入關(guān)鍵字)和By之間的任何文本都被認為是描述性的,除非使用結(jié)果限制關(guān)鍵字之一,如Distinct來在要創(chuàng)建的查詢上設置不同標志,或使用Top/First來限制查詢結(jié)果。

    附錄包含查詢方法主題關(guān)鍵字和查詢方法謂詞關(guān)鍵字的完整列表,包括排序和大小寫修飾符。但是,第一個By充當分隔符,以指示實際條件謂詞的開始。在非?;镜募墑e上,您可以定義實體屬性上的條件,并將它們與And和Or連接起來。

    解析方法的實際結(jié)果取決于為其創(chuàng)建查詢的持久性存儲。然而,有一些一般的事情需要注意:

    表達式通常是屬性遍歷和可以連接的操作符的組合。可以將屬性表達式與“AND”和“OR”結(jié)合使用。對于屬性表達式,還支持Between、LessThan、GreaterThan和Like等操作符。受支持的操作符因數(shù)據(jù)存儲而異,因此請參閱參考文檔的相應部分。

    方法解析器支持為單個屬性(例如,findByLastnameIgnoreCase(…))或支持忽略大小寫的類型的所有屬性(通常是字符串實例-例如,findByLastnameAndFirstnameAllIgnoreCase(…))設置IgnoreCase標志。是否支持忽略大小寫可能因存儲而異,因此請參閱參考文檔中的相關(guān)章節(jié),了解特定于存儲的查詢方法。

    可以通過向引用屬性的查詢方法追加OrderBy子句和提供排序方向(Asc或Desc)來應用靜態(tài)排序。要創(chuàng)建支持動態(tài)排序的查詢方法,請參見“Paging, Iterating Large Results, Sorting”。

    關(guān)于這部分更多內(nèi)容,請參閱官網(wǎng)文檔。

  3. 創(chuàng)建配置類編寫代理實例

    當您使用Spring Data JPA時,您需要定義一個繼承自JpaRepository或其子接口的接口,用于定義對數(shù)據(jù)庫的操作。@EnableJpaRepositories注解的作用是告訴Spring啟用JPA倉庫的自動配置,并掃描指定的包或類路徑,尋找這些定義的JPA倉庫接口。

    在使用@EnableJpaRepositories注解時,通常需要指定basePackages屬性或value屬性,以聲明要掃描的包或類路徑。這樣,Spring就會自動查找該包或類路徑下所有繼承自JpaRepository或其子接口的接口,并自動生成對應的實現(xiàn)類。

    Spring Boot中使用Spring Data JPA時,不寫@EnableJpaRepositories注解和其他相關(guān)配置注解也是可以的。

    Spring Boot默認會自動配置JPA倉庫,包括使用@EnableJpaRepositories注解啟用JPA倉庫的自動配置。

    當您使用Spring Boot的默認約定和項目結(jié)構(gòu)時,它會自動掃描主應用程序類所在的包以及其子包中的所有@Entity注解的實體類和@Repository注解的倉庫接口。然后,Spring Boot會自動生成和注冊這些JPA倉庫的實現(xiàn)類。

    只有當您的項目結(jié)構(gòu)不符合Spring Boot的默認約定,或者您想自定義JPA倉庫的配置時,才需要顯式地使用@EnableJpaRepositories注解和其他相關(guān)配置注解。

    在絕大多數(shù)情況下,您不需要顯式地添加@EnableJpaRepositories注解和其他相關(guān)配置注解,Spring Boot會自動為您配置和管理JPA倉庫。

    @EnableJpaRepositories注解提供了一些可配置的屬性,用于自定義JPA倉庫的行為。以下是一些常用的屬性及其含義:

    • Create: 直接根據(jù)方法名查詢, 如果方法名不符合規(guī)則, 拋出異常, 是在項目啟動的時候就會拋出異常。

    • UseDeclaredQuery: 注解方式, 又叫聲明方式。 通過接口方法上面配置的注解查詢, 注解里面SQL寫錯了, 啟動的時候也會報錯。

    • CreateIfNotFound: 上面兩種方式的綜合體, 先通過注解找, 找不到就通過方法名找(默認)。

    1. basePackages: 指定要掃描的包路徑,以查找繼承自JpaRepository或其子接口的JPA倉庫接口??梢酝ㄟ^字符串數(shù)組的形式指定多個包路徑。

    2. value: 與basePackages屬性作用相同,可以指定要掃描的包路徑。

    3. basePackageClasses: 指定含有JPA倉庫接口的類,Spring會根據(jù)這些類的包路徑進行掃描。可以通過類數(shù)組的形式指定多個類。

    4. entityManagerFactoryRef: 指定要使用的EntityManagerFactory bean的名稱。如果沒有指定,默認會使用一個默認的EntityManagerFactory bean。

    5. transactionManagerRef: 指定要使用的PlatformTransactionManager bean的名稱。如果沒有指定,默認會使用一個默認的PlatformTransactionManager bean。

    6. repositoryImplementationPostfix: 定義自動生成的JPA倉庫實現(xiàn)類的后綴。默認值為Impl,例如,對于UserRepository接口,自動生成的實現(xiàn)類的名稱為UserRepositoryImpl。

    7. namedQueriesLocation: 指定在類路徑下的位置,用于加載命名查詢(Named Queries)??梢允且粋€文件路徑、類路徑或URL。

    8. repositoryFactoryBeanClass: 指定用于創(chuàng)建JPA倉庫的工廠類。默認為org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean。

    9. queryLookupStrategy屬性用于指定JPA倉庫查詢方法的查找策略??梢酝ㄟ^設置不同的值來改變默認行為。

      以下是queryLookupStrategy屬性可用的選項:

2.5自定義SQL

盡管從方法名獲取查詢非常方便,但可能會遇到這樣的情況:方法名解析器不支持想要使用的關(guān)鍵字,或者方法名會變得不必要地難看。因此,您可以通過命名約定使用JPA命名查詢,也可以使用@Query注釋您的查詢方法(請參閱使用Using @Query了解詳細信息)。

public interface UserRepository extends JpaRepository<User, Long> {

?@Query("select u from User u where u.emailAddress = ?1")
?User findByEmailAddress(String emailAddress);
}

2.5.1nativeQuery

在Spring Data JPA中,@Query注解用于指定自定義的JPQL(Java Persistence Query Language)或SQL查詢。nativeQuery參數(shù)用于指示是否使用原生的SQL查詢,可以設置為truefalse。

  • nativeQuerytrue時,表示使用原生的SQL查詢。這意味著可以直接編寫SQL語句,而不是JPQL語句。在這種情況下,查詢語句將會被傳遞給底層數(shù)據(jù)庫進行執(zhí)行,可以使用SQL特有的語法和函數(shù)。這對于復雜的查詢或需要使用數(shù)據(jù)庫特定功能的情況非常有用。

以下是使用nativeQuerytrue的示例:

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
? ?
? ?@Query(value = "SELECT * FROM users WHERE username = ?1", nativeQuery = true)
? ?User findUserByUsername(String username);
}

在上面的示例中,我們使用了nativeQuery = true來指定查詢使用原生的SQL語句來查詢數(shù)據(jù)庫中的users表。在執(zhí)行查詢時,Spring Data JPA會將該查詢語句傳遞給數(shù)據(jù)庫執(zhí)行,并將結(jié)果映射到User對象。

  • nativeQueryfalse時(默認值),表示使用JPQL查詢。JPQL是一種面向?qū)ο蟮牟樵冋Z言,與實體類和數(shù)據(jù)庫字段進行交互。在這種情況下,查詢語句將會由Spring Data JPA解釋和轉(zhuǎn)換為底層數(shù)據(jù)庫支持的SQL,然后執(zhí)行查詢。JPQL提供了一些與獨立于數(shù)據(jù)庫的對象模型交互的功能。

以下是使用nativeQueryfalse的示例(默認值):

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
? ?
? ?@Query("SELECT u FROM User u WHERE u.username = ?1")
? ?User findUserByUsername(String username);
}

在上述示例中,我們使用了JPQL語句SELECT u FROM User u WHERE u.username = ?1來查詢數(shù)據(jù)庫中的User對象。

總結(jié)起來,使用nativeQuerytrue表示使用原生的SQL查詢,而nativeQueryfalse表示使用JPQL查詢。選擇使用哪種查詢?nèi)Q于具體的需求,包括查詢復雜度、數(shù)據(jù)庫特定功能等。

2.5.2傳參方式

@Query注解定義的SQL查詢可以通過多種方式傳遞參數(shù)。下面是幾種常見的傳參方式:

  1. 位置參數(shù)(Positional Parameters):使用?占位符表示參數(shù),按照參數(shù)在方法中聲明的順序傳遞。示例:

    @Query("SELECT u FROM User u WHERE u.id = ?1")
    User findUserById(Long id);

    在上面的例子中,?1表示第一個位置的參數(shù),即方法的id參數(shù)。相應地,你可以使用?2、?3等表示后續(xù)參數(shù)。

  2. 命名參數(shù)(Named Parameters):使用:前綴給參數(shù)命名,然后在查詢語句中使用該命名參數(shù)。示例:

    @Query("SELECT u FROM User u WHERE u.username = :username")
    User findUserByUsername(@Param("username") String username);

    在上述示例中,username是一個命名參數(shù),使用@Param注解指定其值是方法的username參數(shù)。

  3. SpEL表達式參數(shù)(SpEL Expression Parameters):使用#{}將SpEL表達式嵌入查詢語句中。SpEL表達式可以引用方法參數(shù)、類屬性、靜態(tài)常量等。示例:

    @Query("SELECT u FROM User u WHERE u.age > :#{#minAge ?: 18}")
    List<User> findByAgeGreaterThan(@Param("minAge") Integer minAge);

    上面的例子中,SpEL表達式#{#minAge ?: 18}表示取minAge參數(shù)的值作為查詢中的最小年齡,在沒有提供minAge參數(shù)時,默認使用18作為最小年齡。

2.5.3EntityManager

除了上述兩種(解析方法名、@Query)方法定義SQL外,還可以通過EntityManager來執(zhí)行自定義的SQL。

在Spring Data JPA中,EntityManager是JPA(Java Persistence API)的核心接口之一,用于管理實體對象和執(zhí)行與持久化相關(guān)的操作。

EntityManager的主要作用如下:

  1. 實體管理:EntityManager負責跟蹤、管理和操作實體對象的生命周期。它可以創(chuàng)建、讀取、更新和刪除實體對象??梢允褂肊ntityManager的persist、find、merge和remove等方法來執(zhí)行這些操作。

  2. 數(shù)據(jù)庫事務管理:EntityManager在事務管理方面起著重要的作用。通過EntityManager可以啟動、提交或回滾數(shù)據(jù)庫事務。在Spring Data JPA中,事務管理通常由Spring框架提供,但EntityManager是與事務管理密切相關(guān)的一部分。

  3. 緩存管理:EntityManager使用一級緩存來提高性能。一級緩存存儲已經(jīng)查詢或持久化的實體對象,以減少對數(shù)據(jù)庫的訪問。通過實體對象的標識符,可以快速檢索緩存中的實體對象,從而避免對數(shù)據(jù)庫的不必要查詢。

  4. 查詢執(zhí)行:EntityManager可以執(zhí)行查詢語句,包括JPQL查詢和原生SQL查詢。它提供了createQuery、createNamedQuery和createNativeQuery等方法,用于創(chuàng)建并執(zhí)行查詢。

  5. 實體關(guān)系管理:EntityManager支持實體之間的關(guān)系映射和管理。它可以處理實體之間的關(guān)聯(lián)關(guān)系,包括一對一、一對多、多對一和多對多等關(guān)系。通過EntityManager,可以加載和操作關(guān)聯(lián)的實體對象。

需要注意的是,在Spring Data JPA中,EntityManager是由JpaRepository接口和實現(xiàn)類(如SimpleJpaRepository)隱式地創(chuàng)建和使用的。你通常不需要直接使用EntityManager來執(zhí)行基本的CRUD操作,而是使用JpaRepository提供的方法。

我們可以調(diào)用createNativeQuery()方法來執(zhí)行SQL語句,這在項目中也有一定的應用場景,比如對同一組查詢結(jié)果進行avg、max等不同的聚合操作時候,我們可以通過字符串拼接SQL,這樣只需要替換SQL的聚合函數(shù)那部分的字符串即可??傊疄榱松傩㏒QL,EntityManager能提供更加靈活的方法。

3與Hibernate的關(guān)系

Spring Data JPA是對JPA(Java Persistence API)規(guī)范的實現(xiàn),而Hibernate是JPA規(guī)范的一個具體實現(xiàn)。

JPA是Java領(lǐng)域的一種持久化標準,定義了一系列用于對象關(guān)系映射(ORM)的接口和規(guī)則。它提供了一套API和標準注解,用于將Java實體類映射到關(guān)系型數(shù)據(jù)庫。

Hibernate是一個優(yōu)秀的ORM框架,它實現(xiàn)了JPA規(guī)范,并提供了更多的功能和特性。Hibernate提供了豐富的ORM功能,包括對象關(guān)系映射、事務管理、查詢語言和緩存等。

Spring Data JPA是Spring框架中的一個模塊,它提供了一種簡化的方式來訪問和操作數(shù)據(jù)庫,基于JPA規(guī)范實現(xiàn)了一套簡潔的CRUD(Create, Retrieve, Update, Delete)接口。Spring Data JPA封裝了Hibernate等ORM框架的底層細節(jié),提供了更加便捷的持久化操作。

因此,Spring Data JPA使用了Hibernate作為其默認的JPA實現(xiàn)提供商,通過使用Spring Data JPA,您可以更輕松地使用Hibernate進行數(shù)據(jù)庫訪問和操作。同時,Spring Data JPA還支持其他JPA實現(xiàn)提供商,如EclipseLink等,使得您可以更容易地切換和替換不同的JPA實現(xiàn)。

4結(jié)語

關(guān)于Spring Data JPA的更多使用技巧以及技術(shù)點的介紹會在后續(xù)的日常使用中進行補充,如果你覺得其哪個技術(shù)點在項目中很實用,歡迎評論并一起討論。


簡潔持久層開發(fā)—Spring Data JPA的評論 (共 條)

分享到微博請遵守國家法律
五莲县| 都昌县| 亚东县| 万安县| 剑阁县| 瑞金市| 杨浦区| 锡林浩特市| 乌拉特前旗| 当阳市| 凭祥市| 盐边县| 永安市| 定兴县| 巴青县| 溧水县| 镇平县| 阳城县| 锦屏县| 阳朔县| 丰镇市| 榕江县| 建阳市| 黄冈市| 云霄县| 光山县| 宜春市| 沈阳市| 静宁县| 罗甸县| 靖安县| 大连市| 南宫市| 梨树县| 玉树县| 灵宝市| 永登县| 临颍县| 淅川县| 威宁| 南川市|