1. 程式人生 > >Spring Boot2 系列教程(二十三)理解 Spring Data Jpa

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 乾貨!

相關推薦

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

有很多讀者留言希望鬆哥能好好聊聊 Spring Data Jpa! 其實這個話題鬆哥以前零零散散的介紹過,在我的書裡也有介紹過,但是在公眾號中還沒和大夥聊過,因此本文就和大家來仔細聊聊 Spring Data 和 Jpa! 本文大綱: 1. 故事的主角 1.1 Jpa 1.1.1 JPA 是什麼 Jav

Spring Boot2 系列教程(十四)Spring Boot 整合 Jpa

Spring Boot 中的資料持久化方案前面給大夥介紹了兩種了,一個是 JdbcTemplate,還有一個 MyBatis,JdbcTemplate 配置簡單,使用也簡單,但是功能也非常有限,MyBatis 則比較靈活,功能也很強大,據我所知,公司採用 MyBatis 做資料持久化的相當多,但是 MyBat

Spring Boot2 系列教程(十五)Spring Boot 整合 Jpa 多資料來源

本文是 Spring Boot 整合資料持久化方案的最後一篇,主要和大夥來聊聊 Spring Boot 整合 Jpa 多資料來源問題。在 Spring Boot 整合JbdcTemplate 多資料來源、Spring Boot 整合 MyBatis 多資料來源以及 Spring Boot 整合 Jpa 多資料

Spring Boot2 系列教程(十六)Spring Boot 整合 Redis

在 Redis 出現之前,我們的快取框架各種各樣,有了 Redis ,快取方案基本上都統一了,關於 Redis,鬆哥之前有一個系列教程,尚不瞭解 Redis 的小夥伴可以參考這個教程: Redis 教程合集 使用 Java 操作 Redis 的方案很多,Jedis 是目前較為流行的一種方案,除了 Jedi

Spring Boot2 系列教程(十八)Spring Boot 整合 Session 共享

這篇文章是鬆哥的原創,但是在第一次釋出的時候,忘了標記原創,結果被好多號轉發,導致我後來整理的時候自己沒法標記原創了。寫了幾百篇原創技術乾貨了,有一兩篇忘記標記原創進而造成的一點點小小損失也能接受,不過還是要和小夥伴們說明一下。 在傳統的單服務架構中,一般來說,只有一個伺服器,那麼不存在 Session

Spring Boot2 系列教程(十九)Spring Boot 整合 Redis

經過 Spring Boot 的整合封裝與自動化配置,在 Spring Boot 中整合Redis 已經變得非常容易了,開發者只需要引入 Spring Data Redis 依賴,然後簡單配下 redis 的基本資訊,系統就會提供一個 RedisTemplate 供開發者使用,但是今天鬆哥想和大夥聊的不是這種

Spring Boot2 系列教程()建立 Spring Boot 專案的三種方式

我最早是 2016 年底開始寫 Spring Boot 相關的部落格,當時使用的版本還是 1.4.x ,文章發表在 CSDN 上,閱讀量最大的一篇有 43W+,如下圖: 2017 年由於種種原因,就沒有再繼續更新 Spring Boot 相關的部落格了,2018年又去寫書了,也沒更新,現在 Spring

Spring Boot2 系列教程(十一)整合 MyBatis

前面兩篇文章和讀者聊了 Spring Boot 中最簡單的資料持久化方案 JdbcTemplate,JdbcTemplate 雖然簡單,但是用的並不多,因為它沒有 MyBatis 方便,在 Spring+SpringMVC 中整合 MyBatis 步驟還是有點複雜的,要配置多個 Bean,Spring Boo

Spring Boot2 系列教程(六)自定義 Spring Boot 中的 starter

我們使用 Spring Boot,基本上都是沉醉在它 Stater 的方便之中。Starter 為我們帶來了眾多的自動化配置,有了這些自動化配置,我們可以不費吹灰之力就能搭建一個生產級開發環境,有的小夥伴會覺得這個 Starter 好神奇呀!其實 Starter 也都是 Spring + SpringMVC

Spring Boot2 系列教程(三十一)Spring Boot 構建 RESTful 風格應用

RESTful ,到現在相信已經沒人不知道這個東西了吧!關於 RESTful 的概念,我這裡就不做過多介紹了,傳統的 Struts 對 RESTful 支援不夠友好 ,但是 SpringMVC 對於 RESTful 提供了很好的支援,常見的相關注解有: @RestController @GetMapping

Spring Boot2 系列教程(三)理解 Spring Boot 專案中的 parent

前面和大夥聊了 Spring Boot 專案的三種建立方式,這三種建立方式,無論是哪一種,建立成功後,pom.xml 座標檔案中都有如下一段引用: <parent> <groupId>org.springframework.boot</groupId> &l

Spring Boot2 系列教程(四)理解Spring Boot 配置檔案 application.properties

在 Spring Boot 中,配置檔案有兩種不同的格式,一個是 properties ,另一個是 yaml 。 雖然 properties 檔案比較常見,但是相對於 properties 而言,yaml 更加簡潔明瞭,而且使用的場景也更多,很多開源專案都是使用 yaml 進行配置(例如 Hexo)。除了簡潔

Spring Boot2 系列教程(七)理解自動化配置的原理

Spring Boot 中的自動化配置確實夠吸引人,甚至有人說 Spring Boot 讓 Java 又一次煥發了生機,這話雖然聽著有點誇張,但是不可否認的是,曾經臃腫繁瑣的 Spring 配置確實讓人感到頭大,而 Spring Boot 帶來的全新自動化配置,又確實緩解了這個問題。 你要是問這個自動化配置是

Spring Boot2 系列教程(十)@ControllerAdvice 的三種使用場景

嚴格來說,本文並不算是 Spring Boot 中的知識點,但是很多學過 SpringMVC 的小夥伴,對於 @ControllerAdvice 卻並不熟悉,Spring Boot 和 SpringMVC 一脈相承,@ControllerAdvice 在 Spring Boot 中也有廣泛的使用場景,因此本文

Spring Boot2.0系列教程合集、Spring Cloud系列教程合集、Spring Boot常見錯誤合集、Spring Cloud常見錯誤合集

以下內容結合實際專案和工作經驗整理的Spring Boot和Spring Cloud學習教程和一些常見錯誤,希望能幫助到剛學習到童鞋,學習過程遇到問題評論回覆,第一時間會回覆!   Spring Boot2.0系列教程合集 1、Spring Boot2.0系列教程之idea下新

Spring Cloud系列十三) API閘道器服務Spring Cloud Zuul(Finchley.RC2版本)

為什麼使用Spring Cloud Zuul? 通過前幾章的介紹,我們對於Spring Cloud Netflix 下的核心元件已經瞭解了大半,利用這些元件我們已經可以構建一個簡單的微服務架構系統,比如通過使用Spring Cloud Eureka實現高可用的服務註冊中

Spring Boot系列教程:建立第一個web工程 hello world

一.建立工程 建立名稱為"springboot_helloworld"的spring boot工程, new->Spring Starter Project,直接上圖 點選“Fin

Spring Boot2 系列教程(一)純 Java 搭建 SSM 專案

在 Spring Boot 專案中,正常來說是不存在 XML 配置,這是因為 Spring Boot 不推薦使用 XML ,注意,並非不支援,Spring Boot 推薦開發者使用 Java 配置來搭建框架,Spring Boot 中,大量的自動化配置都是通過 Java 配置來實現的,這一套實現方案,我們也可

Spring Boot2 系列教程(五)Spring Boot中的 yaml 配置

搞 Spring Boot 的小夥伴都知道,Spring Boot 中的配置檔案有兩種格式,properties 或者 yaml,一般情況下,兩者可以隨意使用,選擇自己順手的就行了,那麼這兩者完全一樣嗎?肯定不是啦!本文就來和大夥重點介紹下 yaml 配置,最後再來看看 yaml 和 properties 配

Spring Boot2 系列教程(八)Spring Boot 中配置 Https

https 現在已經越來越普及了,特別是做一些小程式或者公眾號開發的時候,https 基本上都是剛需了。 不過一個 https 證書還是挺費錢的,個人開發者可以在各個雲服務提供商那裡申請一個免費的證書。我印象中有效期一年,可以申請 20 個。 今天要和大家聊的是在 Spring Boot 專案中,如何開啟 h