Spring Data JPA ——默認方法使用

初探 SDJ

先讓咱們來鞏固一下 Java 的基礎知識。Java 是面向對象的語言,目的是解決現實生活中的問題,能夠經過使用類來包裝現實生活中的事物成對象、使用屬性來描述對象的特色並使用方法來控制對象的行爲。咱們在 Java 裏一切的操做都是針對對象自己,這也是爲何咱們須要 ORM 來操做存儲在數據庫裏面的「對象」。題外話,對於相似於 Redis 這種基於 Key-Value 存儲的 NoSQL(非關係型數據庫)來講,咱們並不能很顯然地觀察到存儲在裏面的「對象」,由於這種「對象」不一樣於關係型數據庫裏的對象,好比 MySQL。在 MySQL 中,其 Table 就對應 Java 中的類,每一條 Column 對應的就是對象,每個字段對應的就是屬性,固然 MySQL 也有本身的方法,不過不在本文討論範圍內。咱們能夠很直觀地經過 SQL 的查詢語句來觀察「對象」,但在 NoSQL 中,咱們只能經過序列化和反序列化來寫和讀對象。相信我,您寧願看彙編也不想看序列化以後的對象……html

爲了節省時間,關於用什麼和怎麼來構建基本環境不在此贅述,這裏提供 SDJ 的官方指南之一供您參考,只要您能引用 SDJ 的包(org.springframework.data:spring-data-jpa:1.11.6.RELEAS)就行。java

SDJ 對於應用來講最應該關注的就是如下幾個核心接口(按照從子類到父類的繼承順序):mysql

  • JpaRepository —— JPA 協議的具體實現的接口。git

package org.springframework.data.jpa.repository;

    public interface JpaRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {}
  • PagingAndSortingRepository ——分頁和排序的接口。QueryByExampleExecutor ——範例查詢的接口。github

package org.springframework.data.repository;

    public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {}
    
    package org.springframework.data.repository.query;

    public interface QueryByExampleExecutor<T> {}
  • CrudRepository ——通用CRUD操做的接口。redis

package org.springframework.data.repository;
    
    public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> {}

就如同名稱所示,咱們能夠根據狀況繼承特定的接口,不一樣的接口提供不一樣的功能,若是咱們須要分頁和排序,就繼承 PagingAndSortingRepository 接口。但爲了全方位地瞭解 SDJ,本文使用 JpaRepository 接口,因爲其位於繼承樹的最底端,能夠理解成二叉樹裏面的樹葉,因此可使用包括其父類的全部未被重寫方法。固然這樣也有一些不影響功能實現的矛盾點,咱們一會會見到。spring

爲使用默認的接口方法作準備

如同以前所說,咱們須要針對性地經過類來構建一個對象。這裏建立一個用戶類(推薦使用 Lombok 來簡化代碼)。sql

// SDJ 使用 @Entity,NoSQL 使用 @Document。(暫未驗證)
@Entity
public class User {
    // 聲明主鍵。
    @Id
    // 聲明由程序控制主鍵生成策略。
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    // 約束、不可爲空、長度16。
    @Column(unique = true, nullable = false, length = 16)
    private String username;
    @Column(unique = true, nullable = false)
    private String email;
    // 設定默認值。
    private Double balance = 0.0;
    // 不容許更新。
    @Column(updatable = false)
    private Date createTime = new Date();

    // 爲 SDJ 提供一個默認的構造方法。
    public User() {
    }

    // 提供一個不可爲 null 的屬性的構造方法以防止出錯。
    public User(String username, String email) {
        this.username = username;
        this.email = email;
    }

    // 省略一大堆 Getter、Setter 和 ToString 方法。也能夠經過 Lombok 插件以註解的方式大幅度簡化,各大 IDE 均提供!
}

而後建立一個簡單到可能懷疑本身人生接口,繼承自 JpaRepository數據庫

import org.springframework.data.jpa.repository.JpaRepository;
import top.cciradih.spring.data.jpa.entity.User;

// <User, Long> 泛型指定了能接受的對象類型和其主鍵類型,主鍵類型在一些方法裏頗有用。
public interface UserRepository extends JpaRepository<User, Long> {
}

驚不驚喜?意不意外?咱們已經可使用它了!什麼都不用寫!segmentfault

試試接口默認的方法

保存

保存單個對象並返回

// 保存單個對象並返回。
        User savedUser = userRepository.save(new User("Cciradih", "hidarichaochen@gmail.com"));

這是調用 CrudRepository<T, ID extends Serializable> 接口的 <S extends T> S save(S entity) 方法來保存並返回存儲的對象。

保存多個對象並返回

// 保存多個對象並返回。
        List<User> userList = new ArrayList<>();
        User newUser;
        for (int i = 0; i < 10; i++) {
            newUser = new User("Cciradih" + i, "hidarichaochen@gmail.com" + i);
            userList.add(newUser);
        }
        List<User> savedUserList = userRepository.save(userList);

這是調用 JpaRepository<T, ID extends Serializable> 接口的 <S extends T> List<S> save(Iterable<S> entities) 方法來保存多個對象。

查詢

根據主鍵查詢單個對象

// 根據主鍵查詢單個對象。
        User foundUser = userRepository.findOne(1L);

這是調用 CrudRepository<T, ID extends Serializable> 接口的 T findOne(ID id) 方法來查詢單個對象。

查詢所有對象

// 查詢所有對象。
        List<User> foundUserList = userRepository.findAll();

這是調用 JpaRepository<T, ID extends Serializable> 接口的 List<T> findAll() 方法來查所有對象。

根據 ID 字段查詢並排序

import org.springframework.data.domain.Sort;

        // 根據 id 字段查詢並排序,默認是順序(ASC)。
        List<User> foundASCSortedUserList = userRepository.findAll(new Sort("id"));
        // 根據 id 字段倒序查詢(DESC)。
        List<User> foundDESCSortedUserList = userRepository.findAll(new Sort(Sort.Direction.DESC, "id"));

這裏涉及到新的對象—— new Sort("id")。從名字能夠看得出來是爲了排序的,其內部的具體邏輯在本文就不贅述。經過 JpaRepository<T, ID extends Serializable> 接口的 List<T> findAll(Sort sort) 方法來查詢並排序。

範例查詢

import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;

        User user = new User("Cciradih", "hidarichaochen@gmail.com");
        // 1.使用 Example。
        // 建立 Example。
        Example<User> userExample = Example.of(user);
        User foundExampleUser = userRepository.findOne(userExample);
        // 2.使用 ExampleMatcher。
        // 建立 ExampleMatcher。
        ExampleMatcher exampleMatcher = ExampleMatcher.matching()
                // 忽略 id 和 createTime 字段。
                .withIgnorePaths("id", "createTime")
                // 忽略大小寫。
                .withIgnoreCase()
                // 忽略爲空字段。
                .withIgnoreNullValues();
        // 攜帶 ExampleMatcher。
        userExample = Example.of(user, exampleMatcher);
        User foundExampleWithExampleMatcherUser = userRepository.findOne(userExample);

這裏涉及到 Example 和 ExampleMatcher 的使用。

Example 經過其靜態方法 <T> Example<T> of(T probe) 來引入一個 Example<T> 對象。而後就經過 QueryByExampleExecutor<T> 接口的 <S extends T> S findOne(Example<S> example) 方法來查詢符合範例的對象。固然這裏在一般狀況下是查詢不到任何對象的,由於 User 有不一樣的 Id 和某種意義上講不會相同的 createTime。

ExampleMatcher 經過靜態方法 <T> Example<T> of(T probe, ExampleMatcher matcher) 攜帶一個符合的泛型對象和 ExampleMatcher 對象,這裏忽略了 id 和 createTime 字段,因此可以查找到符合條件的對象。

ExampleMatcher 是對 Example 的拓展,這也是爲何我說 SDJ 的控制細膩度依然很高的緣由。

關於 ExampleMatcher 更多的過濾方法能夠自行參照源碼。

分頁查詢

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;

        // 分頁查詢,從 0 頁開始查詢 5 個。
        Page<User> foundUserPage = userRepository.findAll(new PageRequest(0, 5));
        // 分頁表。
        List<User> content = foundUserPage.getContent();
        // 總數量。
        long totalElements = foundUserPage.getTotalElements();
        // 總頁數。
        long totalPages = foundUserPage.getTotalPages();
        // 分頁表大小。
        int size = foundUserPage.getSize();

這裏涉及到新的對象—— new PageRequest(0, 5)。從名字能夠看得出來是爲了分頁的,其內部的具體邏輯在本文就不贅述。經過 PagingAndSortingRepository<T, ID extends Serializable> 接口的 Page<T> findAll(Pageable pageable) 方法來查詢 Page<T> 對象。Page 包含了全部分頁須要的屬性,能夠經過其方法來獲取。

刪除

根據主鍵刪除單個對象

// 根據主鍵刪除單個對象
        userRepository.delete(1L);

這是調用 CrudRepository<T, ID extends Serializable> 接口的 void delete(ID id) 方法來根據主鍵刪除單個對象。

根據對象刪除單個對象

// 根據對象刪除單個對象
        User user = new User("Cciradih", "hidarichaochen@gmail.com");
        userRepository.delete(user);

這是調用 CrudRepository<T, ID extends Serializable> 接口的 void delete(T entity) 方法來根據對象刪除單個對象。和 Example 的例子同樣,也不會刪除任何對象,請自行思考。

刪除所有

// 刪除所有
        userRepository.deleteAll();

這是調用 CrudRepository<T, ID extends Serializable> 接口的 void deleteAll() 方法來根據對象刪除單個對象。

以前提到過的一些不影響功能實現的矛盾點,在這裏體現。因爲 JpaRepository<T, ID extends Serializable> 間接繼承自 CrudRepository<T, ID extends Serializable> 因此咱們能夠同時使用 void deleteAllInBatch()void deleteAll() 方法,二者的功能是同樣的,能夠兩者取其一。想要避免這樣的事,只能在不影響實際業務邏輯的狀況下往上繼承接口。

刪除多個對象

// 刪除多個對象
        List<User> userList = new ArrayList<>();
        User user;
        for (int i = 0; i < 10; i++) {
            user = new User("Cciradih" + i, "hidarichaochen@gmail.com" + i);
            userList.add(user);
        }
        userRepository.delete(userList);

這是調用 CrudRepository<T, ID extends Serializable> 接口的 void delete(Iterable<? extends T> entities) 方法來刪除多個對象。和 Example 的例子同樣,也不會刪除任何對象,請自行思考。

統計對象數量

// 統計對象數量
        long count = userRepository.count();

這是調用 CrudRepository<T, ID extends Serializable> 接口的 long count() 方法來統計對象數量。

根據主鍵判斷對象是否存在

// 判斷對象是否存在
        boolean exists = userRepository.exists(1L);

這是調用 CrudRepository<T, ID extends Serializable> 接口的 boolean exists(ID id) 方法來根據主鍵判斷對象是否存在。

結語

我儘量地列舉了默認方法,但爲了行文方便,其中有些方法是能夠配合使用的,好比 JpaRepository<T, ID extends Serializable> 接口的 <S extends T> List<S> findAll(Example<S> example, Sort sort),能夠同時傳入 Example 和 Sort 對象進行查詢,請自行參照源碼使用。

我但願您可以經過此文對 SDJ 的默認方法達到「知其然」的程度,在軟件開發中,我認爲須要從「知其然」到」知其因此然「再到」造其因此然「的地步,但路須要一步一步走。

  • 源碼倉庫

Learn/Spring Data JPA at master · cciradih/Learn

  • 系列目錄

Spring Data JPA 詳盡指南

  • 參考

Spring Data JPA - Reference Documentation

  • 個人咖啡館

若是您對本文有什麼建議或者問題,歡迎您來個人咖啡館坐坐338147322

相關文章
相關標籤/搜索