Spring Boot2 系列教程(二十三)理解 Spring Data Jpa

有很多讀者留言希望松哥能好好聊聊 Spring Data Jpa! 其實這個話題松哥以前零零散散的介紹過,在我的書里也有介紹過,但是在公眾號中還沒和大夥聊過,因此本文就和大家來仔細聊聊 Spring Data 和 Jpa!

本文大綱:

1. 故事的主角

1.1 Jpa

1.1.1 JPA 是什麼

  1. Java Persistence API:用於對象持久化的 API
  2. Java EE 5.0 平台標準的 ORM 規範,使得應用程序以統一的方式訪問持久層

1.1.2 JPA 和 Hibernate 的關係

  1. JPA 是 Hibernate 的一個抽象(就像 JDBC 和 JDBC 驅動的關係);
  2. JPA 是規範:JPA 本質上就是一種 ORM 規範,不是 ORM 框架,這是因為 JPA 並未提供 ORM 實現,它只是制訂了一些規範,提供了一些編程的 API 接口,但具體實現則由 ORM 廠商提供實現;
  3. Hibernate 是實現:Hibernate 除了作為 ORM 框架之外,它也是一種 JPA 實現
  4. 從功能上來說, JPA 是 Hibernate 功能的一個子集

1.1.3 JPA 的供應商

JPA 的目標之一是制定一個可以由很多供應商實現的 API,Hibernate 3.2+、TopLink 10.1+ 以及 OpenJPA 都提供了 JPA 的實現,Jpa 供應商有很多,常見的有如下四種:

  1. Hibernate:JPA 的始作俑者就是 Hibernate 的作者,Hibernate 從 3.2 開始兼容 JPA。
  2. OpenJPA:OpenJPA 是 Apache 組織提供的開源項目。
  3. TopLink:TopLink 以前需要收費,如今開源了。
  4. EclipseLink

1.1.4 JPA 的優勢

  1. 標準化: 提供相同的 API,這保證了基於 JPA 開發的企業應用能夠經過少量的修改就能夠在不同的 JPA 框架下運行。
  2. 簡單易用,集成方便: JPA 的主要目標之一就是提供更加簡單的編程模型,在 JPA 框架下創建實體和創建 Java 類一樣簡單,只需要使用 javax.persistence.Entity 進行註解;JPA 的框架和接口也都非常簡單。
  3. 可媲美JDBC的查詢能力: JPA的查詢語言是面向對象的,JPA 定義了獨特的JPQL,而且能夠支持批量更新和修改、JOIN、GROUP BY、HAVING 等通常只有 SQL 才能夠提供的高級查詢特性,甚至還能夠支持子查詢。
  4. 支持面向對象的高級特性: JPA 中能夠支持面向對象的高級特性,如類之間的繼承、多態和類之間的複雜關係,最大限度的使用面向對象的模型

1.1.5 JPA 包含的技術

  1. ORM 映射元數據:JPA 支持 XML 和 JDK 5.0 註解兩種元數據的形式,元數據描述對象和表之間的映射關係,框架據此將實體對象持久化到數據庫表中。
  2. JPA 的 API:用來操作實體對象,執行CRUD操作,框架在後台完成所有的事情,開發者從繁瑣的 JDBC 和 SQL 代碼中解脫出來。
  3. 查詢語言(JPQL):這是持久化操作中很重要的一個方面,通過面向對象而非面向數據庫的查詢語言查詢數據,避免程序和具體的 SQL 緊密耦合。

1.2 Spring Data

Spring Data 是 Spring 的一個子項目。用於簡化數據庫訪問,支持NoSQL 和 關係數據存儲。其主要目標是使數據庫的訪問變得方便快捷。Spring Data 具有如下特點:

  • SpringData 項目支持 NoSQL 存儲:
    1. MongoDB (文檔數據庫)
    2. Neo4j(圖形數據庫)
    3. Redis(鍵/值存儲)
    4. Hbase(列族數據庫)
  • SpringData 項目所支持的關係數據存儲技術:
    1. JDBC
    2. JPA
  • Spring Data Jpa 致力於減少數據訪問層 (DAO) 的開發量. 開發者唯一要做的,就是聲明持久層的接口,其他都交給 Spring Data JPA 來幫你完成
  • 框架怎麼可能代替開發者實現業務邏輯呢?比如:當有一個UserDao.findUserById() 這樣一個方法聲明,大致應該能判斷出這是根據給定條件的 ID 查詢出滿足條件的 User 對象。Spring Data JPA 做的便是規範方法的名字,根據符合規範的名字來確定方法需要實現什麼樣的邏輯。

2. 主角的故事

2.1 Jpa 的故事

為了讓大夥徹底把這兩個東西學會,這裏我就先來介紹單純的 Jpa 使用,然後我們再結合 Spring Data 來看 Jpa 如何使用。

整體步驟如下:

  • 1.使用 IntelliJ IDEA 創建項目,創建時選擇 JavaEE Persistence ,如下:
  • 2.創建成功后,添加依賴 jar,由於 Jpa 只是一個規範,因此我們說用 Jpa 實際上必然是用 Jpa 的某一種實現,那麼是哪一種實現呢?當然就是 Hibernate 了,所以添加的 jar,實際上來自 Hibernate,如下:
  • 3.添加實體類

接下來在項目中添加實體類,如下:

@Entity(name = "t_book")
public class Book {
    private Long id;
    private String name;
    private String author;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    public Long getId() {
        return id;
    }
    // 省略其他getter/setter
}

首先 @Entity 註解表示這是一個實體類,那麼在項目啟動時會自動針對該類生成一張表,默認的表名為類名,@Entity 註解的 name 屬性表示自定義生成的表名。@Id 註解表示這個字段是一個 id,@GeneratedValue 註解表示主鍵的自增長策略,對於類中的其他屬性,默認都會根據屬性名在表中生成相應的字段,字段名和屬性名相同,如果開發者想要對字段進行定製,可以使用 @Column 註解,去配置字段的名稱,長度,是否為空等等。

  • 4.創建 persistence.xml 文件

JPA 規範要求在類路徑的 META-INF 目錄下放置 persistence.xml,文件的名稱是固定的

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
    <persistence-unit name="NewPersistenceUnit" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <class>org.javaboy.Book</class>
        <properties>
            <property name="hibernate.connection.url"
                      value="jdbc:mysql:///jpa01?useUnicode=true&amp;characterEncoding=UTF-8"/>
            <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
            <property name="hibernate.connection.username" value="root"/>
            <property name="hibernate.connection.password" value="123"/>
            <property name="hibernate.archive.autodetection" value="class"/>
            <property name="hibernate.show_sql" value="true"/>
            <property name="hibernate.format_sql" value="true"/>
            <property name="hibernate.hbm2ddl.auto" value="update"/>
        </properties>
    </persistence-unit>
</persistence>

注意:

  1. persistence-unit 的 name 屬性用於定義持久化單元的名字, 必填。
  2. transaction-type:指定 JPA 的事務處理策略。RESOURCE_LOCAL:默認值,數據庫級別的事務,只能針對一種數據庫,不支持分佈式事務。如果需要支持分佈式事務,使用JTA:transaction-type=”JTA”
  3. class 節點表示顯式的列出實體類
  4. properties 中的配置分為兩部分:數據庫連接信息以及Hibernate信息
  • 5.執行持久化操作
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("NewPersistenceUnit");
EntityManager manager = entityManagerFactory.createEntityManager();
EntityTransaction transaction = manager.getTransaction();
transaction.begin();
Book book = new Book();
book.setAuthor("羅貫中");
book.setName("三國演義");
manager.persist(book);
transaction.commit();
manager.close();
entityManagerFactory.close();

這裏首先根據配置文件創建出來一個 EntityManagerFactory ,然後再根據 EntityManagerFactory 的實例創建出來一個 EntityManager ,然後再開啟事務,調用 EntityManager 中的 persist 方法執行一次持久化操作,最後提交事務,執行完這些操作后,數據庫中舊多出來一個 t_book 表,並且表中有一條數據。

2.1.1 關於 JPQL

  1. JPQL 語言,即 Java Persistence Query Language 的簡稱。JPQL 是一種和 SQL 非常類似的中間性和對象化查詢語言,它最終會被編譯成針對不同底層數據庫的 SQL 查詢,從而屏蔽不同數據庫的差異。JPQL語言的語句可以是 select 語句、update 語句或 delete 語句,它們都通過 Query 接口封裝執行。
  2. Query接口封裝了執行數據庫查詢的相關方法。調用 EntityManager 的 createQuery、create NamedQuery 及 createNativeQuery 方法可以獲得查詢對象,進而可調用 Query 接口的相關方法來執行查詢操作。
  3. Query接口的主要方法如下:
  • int executeUpdate(); | 用於執行update或delete語句。
  • List getResultList(); | 用於執行select語句並返回結果集實體列表。
  • Object getSingleResult(); | 用於執行只返回單個結果實體的select語句。
  • Query setFirstResult(int startPosition); | 用於設置從哪個實體記錄開始返回查詢結果。
  • Query setMaxResults(int maxResult); | 用於設置返回結果實體的最大數。與setFirstResult結合使用可實現分頁查詢。
  • Query setFlushMode(FlushModeType flushMode); | 設置查詢對象的Flush模式。參數可以取2個枚舉值:FlushModeType.AUTO 為自動更新數據庫記錄,FlushMode Type.COMMIT 為直到提交事務時才更新數據庫記錄。
  • setHint(String hintName, Object value); | 設置與查詢對象相關的特定供應商參數或提示信息。參數名及其取值需要參考特定 JPA 實現庫提供商的文檔。如果第二個參數無效將拋出IllegalArgumentException異常。
  • setParameter(int position, Object value); | 為查詢語句的指定位置參數賦值。Position 指定參數序號,value 為賦給參數的值。
  • setParameter(int position, Date d, TemporalType type); | 為查詢語句的指定位置參數賦 Date 值。Position 指定參數序號,value 為賦給參數的值,temporalType 取 TemporalType 的枚舉常量,包括 DATE、TIME 及 TIMESTAMP 三個,,用於將 Java 的 Date 型值臨時轉換為數據庫支持的日期時間類型(java.sql.Date、java.sql.Time及java.sql.Timestamp)。
  • setParameter(int position, Calendar c, TemporalType type); | 為查詢語句的指定位置參數賦 Calenda r值。position 指定參數序號,value 為賦給參數的值,temporalType 的含義及取捨同前。
  • setParameter(String name, Object value); | 為查詢語句的指定名稱參數賦值。
  • setParameter(String name, Date d, TemporalType type); | 為查詢語句的指定名稱參數賦 Date 值,用法同前。
  • setParameter(String name, Calendar c, TemporalType type); | 為查詢語句的指定名稱參數設置Calendar值。name為參數名,其它同前。該方法調用時如果參數位置或參數名不正確,或者所賦的參數值類型不匹配,將拋出 IllegalArgumentException 異常。

2.1.2 JPQL 舉例

和在 SQL 中一樣,JPQL 中的 select 語句用於執行查詢。其語法可表示為:

select_clause form_clause [where_clause] [groupby_clause] [having_clause] [orderby_clause]

其中:

  1. from 子句是查詢語句的必選子句。
  2. select 用來指定查詢返回的結果實體或實體的某些屬性。
  3. from 子句聲明查詢源實體類,並指定標識符變量(相當於SQL表的別名)。
  4. 如果不希望返回重複實體,可使用關鍵字 distinct 修飾。select、from 都是 JPQL 的關鍵字,通常全大寫或全小寫,建議不要大小寫混用。

在 JPQL 中,查詢所有實體的 JPQL 查詢語句很簡單,如下:

select o from Order o

select o from Order as o

這裏關鍵字 as 可以省去,標識符變量的命名規範與 Java 標識符相同,且區分大小寫,調用 EntityManager 的 createQuery() 方法可創建查詢對象,接着調用 Query 接口的 getResultList() 方法就可獲得查詢結果集,如下:

Query query = entityManager.createQuery( "select o from Order o"); 
List orders = query.getResultList();
Iterator iterator = orders.iterator();
while(iterator.hasNext() ) {
  // 處理Order
}

其他方法的與此類似,這裏不再贅述。

2.2 Spring Data 的故事

在 Spring Boot 中,Spring Data Jpa 官方封裝了太多東西了,導致很多人用的時候不知道底層到底是怎麼配置的,本文就和大夥來看看在手工的 Spring 環境下,Spring Data Jpa 要怎麼配置,配置完成后,用法和 Spring Boot 中的用法是一致的。

2.2.1 基本環境搭建

首先創建一個普通的 Maven 工程,並添加如下依賴:

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-orm</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-oxm</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.27</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-support</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-expression</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-core</artifactId>
        <version>5.2.12.Final</version>
    </dependency>
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-jpamodelgen</artifactId>
        <version>5.2.12.Final</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.0.29</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-jpa</artifactId>
        <version>1.11.3.RELEASE</version>
    </dependency>
</dependencies>

這裏除了 Jpa 的依賴之外,就是 Spring Data Jpa 的依賴了。

接下來創建一個 User 實體類,創建方式參考 Jpa 中實體類的創建方式,這裏不再贅述。

接下來在 resources 目錄下創建一個 applicationContext.xml 文件,並配置Spring 和 Jpa,如下:

<context:property-placeholder location="classpath:db.properties"/>
<context:component-scan base-package="org.javaboy"/>
<bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
    <property name="driverClassName" value="${db.driver}"/>
    <property name="url" value="${db.url}"/>
    <property name="username" value="${db.username}"/>
    <property name="password" value="${db.password}"/>
</bean>
<bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="entityManagerFactory">
    <property name="dataSource" ref="dataSource"/>
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
    </property>
    <property name="packagesToScan" value="org.javaboy.model"/>
    <property name="jpaProperties">
        <props>
            <prop key="hibernate.show_sql">true</prop>
            <prop key="hibernate.format_sql">true</prop>
            <prop key="hibernate.hbm2ddl.auto">update</prop>
            <prop key="hibernate.dialect">org.hibernate.dialect.MySQL57Dialect</prop>
        </props>
    </property>
</bean>
<bean class="org.springframework.orm.jpa.JpaTransactionManager" id="transactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- 配置jpa -->
<jpa:repositories base-package="org.javaboy.dao"
                  entity-manager-factory-ref="entityManagerFactory"/>

這裏和 Jpa 相關的配置主要是三個:

  • 一個是 entityManagerFactory
  • 一個是 Jpa 的事務
  • 一個是配置 dao 的位置

配置完成后,就可以在 org.javaboy.dao 包下創建相應的 Repository 了,如下:

public interface UserDao extends Repository<User, Long> {
    User getUserById(Long id);
}

getUserById 表示根據 id 去查詢 User 對象,只要我們的方法名稱符合類似的規範,就不需要寫 SQL,具體的規範一會來說。好了,接下來,創建 Service 和 Controller 來調用這個方法,如下:

@Service
@Transactional
public class UserService {
    @Resource
    UserDao userDao;

    public User getUserById(Long id) {
        return userDao.getUserById(id);
    }
}
public void test1() {
    ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserService userService = ctx.getBean(UserService.class);
    User user = userService.getUserById(1L);
    System.out.println(user);
}

這樣,就可以查詢到 id 為 1 的用戶了。

2.2.2 Repository

上文我們自定義的 UserDao 實現了 Repository 接口,這個 Repository 接口是什麼來頭呢?

首先來看 Repository 的一個繼承關係圖:

可以看到,實現類不少。那麼到底如何理解 Repository 呢?

  1. Repository 接口是 Spring Data 的一個核心接口,它不提供任何方法,開發者需要在自己定義的接口中聲明需要的方法 public interface Repository<T, ID extends Serializable> { }
  2. 若我們定義的接口繼承了 Repository, 則該接口會被 IOC 容器識別為一個 Repository Bean,進而納入到 IOC 容器中,進而可以在該接口中定義滿足一定規範的方法。
  3. Spring Data可以讓我們只定義接口,只要遵循 Spring Data 的規範,就無需寫實現類。
  4. 與繼承 Repository 等價的一種方式,就是在持久層接口上使用 @RepositoryDefinition 註解,併為其指定 domainClass 和 idClass 屬性。像下面這樣:
@RepositoryDefinition(domainClass = User.class, idClass = Long.class)
public interface UserDao
{
    User findById(Long id);
    List<User> findAll();
}

基礎的 Repository 提供了最基本的數據訪問功能,其幾個子接口則擴展了一些功能,它的幾個常用的實現類如下:

  • CrudRepository: 繼承 Repository,實現了一組 CRUD 相關的方法
  • PagingAndSortingRepository: 繼承 CrudRepository,實現了一組分頁排序相關的方法
  • JpaRepository: 繼承 PagingAndSortingRepository,實現一組 JPA 規範相關的方法
  • 自定義的 XxxxRepository 需要繼承 JpaRepository,這樣的 XxxxRepository 接口就具備了通用的數據訪問控制層的能力。
  • JpaSpecificationExecutor: 不屬於Repository 體系,實現一組 JPA Criteria 查詢相關的方法

2.2.3 方法定義規範

2.2.3.1 簡單條件查詢

  • 按照 Spring Data 的規範,查詢方法以 find | read | get 開頭
  • 涉及條件查詢時,條件的屬性用條件關鍵字連接,要注意的是:條件屬性以首字母大寫

例如:定義一個 Entity 實體類:

class User{ 
   private String firstName; 
   private String lastName; 
}

使用 And 條件連接時,條件的屬性名稱與個數要與參數的位置與個數一一對應,如下:

findByLastNameAndFirstName(String lastName,String firstName);
  • 支持屬性的級聯查詢. 若當前類有符合條件的屬性, 則優先使用, 而不使用級聯屬性. 若需要使用級聯屬性, 則屬性之間使用 _ 進行連接.

查詢舉例:

  • 按照 id 查詢
User getUserById(Long id);
User getById(Long id);
  • 查詢所有年齡小於 90 歲的人
List<User> findByAgeLessThan(Long age);
  • 查詢所有姓趙的人
List<User> findByUsernameStartingWith(String u);
  • 查詢所有姓趙的、並且 id 大於 50 的人
List<User> findByUsernameStartingWithAndIdGreaterThan(String name, Long id);
  • 查詢所有姓名中包含”上”字的人
List<User> findByUsernameContaining(String name);
  • 查詢所有姓趙的或者年齡大於 90 歲的
List<User> findByUsernameStartingWithOrAgeGreaterThan(String name, Long age);
  • 查詢所有角色為 1 的用戶
List<User> findByRole_Id(Long id);

2.2.3.2 支持的關鍵字

支持的查詢關鍵字如下圖:

2.2.3.3 查詢方法流程解析

為什麼寫上方法名,JPA就知道你想幹嘛了呢?假如創建如下的查詢:findByUserDepUuid(),框架在解析該方法時,首先剔除 findBy,然後對剩下的屬性進行解析,假設查詢實體為Doc:

  1. 先判斷 userDepUuid (根據 POJO 規範,首字母變為小寫)是否為查詢實體的一個屬性,如果是,則表示根據該屬性進行查詢;如果沒有該屬性,繼續第二步;
  2. 從右往左截取第一個大寫字母開頭的字符串(此處為 Uuid),然後檢查剩下的字符串是否為查詢實體的一個屬性,如果是,則表示根據該屬性進行查詢;如果沒有該屬性,則重複第二步,繼續從右往左截取;最後假設 user 為查詢實體的一個屬性;
  3. 接着處理剩下部分(DepUuid),先判斷 user 所對應的類型是否有depUuid屬性,如果有,則表示該方法最終是根據 “ Doc.user.depUuid” 的取值進行查詢;否則繼續按照步驟 2 的規則從右往左截取,最終表示根據 “Doc.user.dep.uuid” 的值進行查詢。
  4. 可能會存在一種特殊情況,比如 Doc 包含一個 user 的屬性,也有一個 userDep 屬性,此時會存在混淆。可以明確在屬性之間加上 “_” 以顯式表達意圖,比如 “findByUser_DepUuid()” 或者 “findByUserDep_uuid()”
  5. 還有一些特殊的參數:例如分頁或排序的參數:
Page<UserModel> findByName(String name, Pageable pageable);
List<UserModel> findByName(String name, Sort sort);

2.2.3.4 @Query 註解

有的時候,這裏提供的查詢關鍵字並不能滿足我們的查詢需求,這個時候就可以使用 @Query 關鍵字,來自定義查詢 SQL,例如查詢 Id 最大的 User:

@Query("select u from t_user u where id=(select max(id) from t_user)")
User getMaxIdUser();

如果查詢有參數的話,參數有兩種不同的傳遞方式,

  • 利用下標索引傳參,索引參數如下所示,索引值從1開始,查詢中 ”?X” 個數需要與方法定義的參數個數相一致,並且順序也要一致:
@Query("select u from t_user u where id>?1 and username like ?2")
List<User> selectUserByParam(Long id, String name);
  • 命名參數(推薦):這種方式可以定義好參數名,賦值時採用@Param(“參數名”),而不用管順序:
@Query("select u from t_user u where id>:id and username like :name")
List<User> selectUserByParam2(@Param("name") String name, @Param("id") Long id);

查詢時候,也可以是使用原生的 SQL 查詢,如下:

@Query(value = "select * from t_user",nativeQuery = true)
List<User> selectAll();

2.2.3.5 @Modifying 註解

涉及到數據修改操作,可以使用 @Modifying 註解,@Query 與 @Modifying 這兩個 annotation 一起聲明,可定義個性化更新操作,例如涉及某些字段更新時最為常用,示例如下:

@Modifying
@Query("update t_user set age=:age where id>:id")
int updateUserById(@Param("age") Long age, @Param("id") Long id);

注意:

  1. 可以通過自定義的 JPQL 完成 UPDATE 和 DELETE 操作. 注意: JPQL 不支持使用 INSERT
  2. 方法的返回值應該是 int,表示更新語句所影響的行數
  3. 在調用的地方必須加事務,沒有事務不能正常執行
  4. 默認情況下, Spring Data 的每個方法上有事務, 但都是一個只讀事務. 他們不能完成修改操作

說到這裏,再來順便說說 Spring Data 中的事務問題:

  1. Spring Data 提供了默認的事務處理方式,即所有的查詢均聲明為只讀事務。
  2. 對於自定義的方法,如需改變 Spring Data 提供的事務默認方式,可以在方法上添加 @Transactional 註解。
  3. 進行多個 Repository 操作時,也應該使它們在同一個事務中處理,按照分層架構的思想,這部分屬於業務邏輯層,因此,需要在Service 層實現對多個 Repository 的調用,並在相應的方法上聲明事務。

好了,關於Spring Data Jpa 本文就先說這麼多。

關注公眾號【江南一點雨】,專註於 Spring Boot+微服務以及前後端分離等全棧技術,定期視頻教程分享,關注后回復 Java ,領取松哥為你精心準備的 Java 乾貨!

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包”嚨底家”

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

小三通海運與一般國際貿易有何不同?

小三通快遞通關作業有哪些?

您可能也會喜歡…