1. 程式人生 > >第3章 整合Spring Data JPA

第3章 整合Spring Data JPA

3.1 Spring Data JPA介紹

本節主要介紹Spring Data JPA是什麼、Spring Data JPA核心介面Repository、核心介面間的繼承關係圖。

3.1.1 Spring Data JPA介紹

JPA(Java Persistence API)是Sun官方提出的Java持久化規範。所謂規範即只定義標準規則,不提供實現。而JPA的主要實現有Hibernate、EclipseLink、OpenJPA等。JPA是一套規範,不是一套產品。Hibernate是一套產品,如果這些產品實現了JPA規範,那麼我們可以叫它們為JPA的實現產品。 Spring Data JPA是Spring Data的一個子專案,它通過提供基於JPA的Respository,極大地減少了JPA作為資料訪問方案的程式碼量。通過Spring Data JPA框架,開發者可以省略實現持久層業務邏輯的工作,唯一要做的,就只是宣告持久層的介面,其它都交給 Spring Data JPA 來幫你完成。

3.1.2 核心介面Repository

Spring Data JPA最頂層介面是Repository,該介面是所有Repository類的父類。具體程式碼如下:

package org.springframework.data.repository;
import java.io.Serializable;
public interface Repository<T, ID extends Serializable> {

}

Repository類下沒有任何的介面,只是一個空類。Repository介面的子類有CrudRepository、PagingAndSortingRepository、JpaRepository等。其中CrudRepository類提供了基本的增刪改查等介面,PagingAndSortingRepository類提供了基本的分頁和排序等介面,而JpaRepository是CrudRepository和PagingAndSortingRepository的子類,繼承了它們的所有介面。所以在真實的專案當中,我們都是通過實現JpaRepository或者其子類進行基本的資料庫操作。JpaRepository具體程式碼如下:

@NoRepositoryBean
public interface JpaRepository extends PagingAndSortingRepository<T, ID> {
    List<T> findAll();
    List<T> findAll(Sort var1);
    List<T> findAll(Iterable<ID> var1);
    <S extends T> List<S> save(Iterable<S> var1);
    void flush();
    <S extends T> S saveAndFlush(S var1);
    void deleteInBatch(Iterable<T> var1);
    void deleteAllInBatch();
    T getOne(ID var1);
    <S extends T> List<S> findAll(Example<S> var1);
    <S extends T> List<S> findAll(Example<S> var1, Sort var2);
}

@NoRepositoryBean:使用該註解標明,此介面不是一個Repository Bean。

3.1.3 介面繼承關係圖

Repository介面間的繼承關係如圖3-1所示。通過該繼承圖,我們可以清楚知道介面間的整合關係。在專案中,我們一般都是實現JapRepository類,加上自己定義的業務方法,來完成我們的業務開發。

在這裡插入圖片描述 圖3-1 Repository介面間整合關係

3.2 整合Spring Data JPA

本節主要介紹如何在Spring Boot中整合Spring Data JPA,服務層類開發,如何通過Spring Data JPA實現基本增刪改查功能,以及自定義查詢方法等內容。

3.2.1 引入依賴

在Spring Boot中整合Spring Data JPA,首先需要在pom.xml檔案中引入所需的依賴,具體程式碼如下:

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

在之前的章節中,我們已經在開發工具中安裝好Maven Helper外掛,所以大家可以通過該外掛檢視目前引入的所有依賴,具體如圖3-2所示。

在這裡插入圖片描述 圖3-2 my-spring-boot專案目錄 圖3-3 Maven Helper檢視pom依賴包

3.2.2 繼承JpaRepository

在pom.xml檔案中引入依賴之後,我們在目錄/src/main/java/com.example.demo.repository下開發一個AyUserRepository類,如圖3-3所示,具體程式碼如下:

/**
 * 描述:使用者Repository
 * @author 阿毅
 * @date   2017/10/14.
 */
public interface AyUserRepository extends JpaRepository<AyUser,String>{

}

與此同時,我們需要AyUser實體類下新增@Entity和@Id註解,具體程式碼如下:

/**
 * 描述:使用者表
 * @Author 阿毅
 * @date   2017/10/8.
 */
@Entity
@Table(name = "ay_user")
public class AyUser {
    //主鍵
    @Id
    private String id;
    //使用者名稱
    private String name;
    //密碼
    private String password;
}

@Entity:每個持久化POJO類都是一個實體Bean, 通過在類的定義中使用 @Entity 註解來進行宣告。 @Table:宣告此物件對映到資料庫的資料表。該註釋不是必須的,如果沒有則系統使用預設值(實體的短類名)。 @Id:指定表的主鍵。

3.2.3 服務層類實現

我們在my-spring-boot專案下繼續開發服務層介面類和實現類:AyUserService和AyUserServiceImpl類。它們分別存放在目錄/src/main/java/com.example.demo.service 和/src/main/java/com.example.demo.service.impl下。具體程式碼如下所示:

/**
 * 描述:使用者服務層介面
 * @author 阿毅
 * @date   2017/10/14
 */
public interface AyUserService {
AyUser findById(String id);
    List<AyUser> findAll();
    AyUser save(AyUser ayUser);
    void delete(String id);
}

介面類AyUserService定義了4個介面,findById和findAll用來查詢單個和所有資料,delete用來刪除資料,save同時具備儲存和更新資料的功能。介面實現類AyUserServiceImpl程式碼如下:

/**
 * 描述:使用者服務層實現類
 * @author 阿毅
 * @date   2017/10/14
 */
@Service
public class AyUserServiceImpl implements AyUserService{

    @Resource
    private AyUserRepository ayUserRepository;

	    @Override
    public AyUser findById(String id){
        return ayUserRepository.findById(id).get();
    }

    @Override
    public List<AyUser> findAll() {
        return ayUserRepository.findAll();
    }

    @Override
    public AyUser save(AyUser ayUser) {
        return ayUserRepository.save(ayUser);
    }

    @Override
    public void delete(String id) {
        ayUserRepository.deleteById(id);
    }
}

@ Service:Spring Boot會自動掃描到@Component註解的類,並把這些類納入進Spring容器中管理。也可以用@Component註解,只是@Service註解更能表明該類是服務層類。 @Component:泛指元件,當元件不好歸類的時候,我們可以使用這個註解進行標註。 @Repository:持久層元件,用於標註資料訪問元件,即DAO元件 。 @Resource:這個註解屬於J2EE的,預設安照名稱進行裝配,名稱可以通過name屬性進行指定。如果沒有指定name屬性,當註解寫在欄位上時,預設取欄位名進行查詢。如果註解寫在setter方法上預設取屬性名進行裝配。 當找不到與名稱匹配的bean時才按照型別進行裝配。但是需要注意的是,如果name屬性一旦指定,就只會按照名稱進行裝配。具體程式碼如下:

@Resource(name = "ayUserRepository")
private AyUserRepository ayUserRepository;

@Autowired:這個註解是屬於Spring的,預設按型別裝配。預設情況下要求依賴物件必須存在,如果要允許null 值,可以設定它的required屬性為false,如:@Autowired(required=false) ,如果我們想使用名稱裝配可以結合@Qualifier註解進行使用。具體程式碼如下:

@Autowired
@Qualifier("ayUserRepository")
private AyUserRepository ayUserRepository;

3.2.4 增刪改查分頁簡單實現

上一節,我們已經在服務層類AyUserService開發完增刪改查方法。這一節,我們將繼續在類中新增分頁介面,具體程式碼如下:

/**
 * 描述:使用者服務層介面
 * @author 阿毅
 * @date   2017/10/14
 */
public interface AyUserService {
AyUser findById(String id);
    List<AyUser> findAll();
    AyUser save(AyUser ayUser);
    void delete(String id);
    //分頁
	    Page<AyUser> findAll(Pageable pageable);
}

Pageable:這是一個分頁介面,查詢時候我們只需要傳入一個 Pageable介面的實現類,指定pageNumber和pageSize即可。pageNumber為第幾頁,而pageSize為每頁大小。 Page:分頁查詢結果會封裝在該類中,Page介面實現Slice介面,通過檢視其原始碼可知。我們通過呼叫getTotalPages和 getContent等方法,可以方便獲得總頁數和查詢的記錄。Page介面和Slice介面原始碼如下:

public interface Page<T> extends Slice<T> {
    int getTotalPages();
    long getTotalElements();
    <S> Page<S> map(Converter<? super T, ? extends S> var1);
}

public interface Slice<T> extends Iterable<T> {
    int getNumber();
    int getSize();
    int getNumberOfElements();
    List<T> getContent();
    boolean hasContent();
    Sort getSort();
    boolean isFirst();
    boolean isLast();
    boolean hasNext();
    boolean hasPrevious();
    Pageable nextPageable();
    Pageable previousPageable();
    <S> Slice<S> map(Converter<? super T, ? extends S> var1);
}

分頁方法定義好之後,我們在類AyUserServiceImpl中實現該方法,具體程式碼如下:

@Override
public Page<AyUser> findAll(Pageable pageable) {
    return ayUserRepository.findAll(pageable);
}

3.2.5 自定義查詢方法

我們除了使用JpaRepository介面提供的增刪改查分頁等方法之外,還可以自定義查詢方法。我們在AyUserRepository類中新增幾個自定義查詢方法,具體程式碼如下:

/**
 * 描述:使用者Repository
 * @author 阿毅
 * @date   2017/10/14.
 */
public interface AyUserRepository extends JpaRepository<AyUser,String>{

/**
     * 描述:通過名字相等查詢,引數為 name
     * 相當於:select u from ay_user u where u.name = ?1
     */
    List<AyUser> findByName(String name);

    /**
     * 描述:通過名字like查詢,引數為 name
     * 相當於:select u from ay_user u where u.name like ?1
     */
    List<AyUser> findByNameLike(String name);

    /**
     * 描述:通過主鍵id集合查詢,引數為 id集合
     * 相當於:select u from ay_user u where id in(?,?,?)
     * @param ids
     */
    List<AyUser> findByIdIn(Collection<String> ids); }

在AyUserRepository中,我們自定義了3個查詢的方法。從程式碼可以看出,Spring Data JPA為我們約定了一系列的規範,只要我們按照規範編寫程式碼,Spring Data JPA就會根據程式碼翻譯成相關的SQL語句,進行資料庫查詢。比如我們可以使用findBy、Like、In等關鍵字。其中findBy可以用read、readBy、query、queryBy、get、getBy來代替。關於查詢關鍵字的更多內容,大家可以到官網(https://docs.spring.io/spring-data/data-jpa/docs/current/reference/html/)檢視,裡面有詳細的內容介紹,這裡就不一一列舉了。 AyUserRepository類中自定義查詢方法開發完成之後,我們分別在類AyUserService和類AyUserServiceImpl中呼叫它們。 AyUserService繼續新增這3個方法,具體程式碼如下:

List<AyUser> findByName(String name);
List<AyUser> findByNameLike(String name);
List<AyUser> findByIdIn(Collection<String> ids);

    AyUserServiceImpl類新增這3個方法,具體程式碼如下:
@Override
public List<AyUser> findByName(String name){
   return ayUserRepository.findByName(name);
}
@Override
public List<AyUser> findByNameLike(String name){
  return ayUserRepository.findByNameLike(name);
}
@Override
public List<AyUser> findByIdIn(Collection<String> ids){
   return ayUserRepository.findByIdIn(ids);
}

@Override註解不可去掉哦,它幫助我們校驗介面方法是否被誤改。

3.3 整合測試

3.3.1 測試用例開發

我們在測試類MySpringBootApplicationTests中新增如下程式碼:

@Resource
	private AyUserService ayUserService;

	@Test
	public void testRepository(){
		//查詢所有資料
		List<AyUser> userList =  ayUserService.findAll();
		System.out.println("findAll() :" + userList.size());
		//通過name查詢資料
		List<AyUser> userList2 = ayUserService.findByName("阿毅");
		System.out.println("findByName() :" + userList2.size());
		Assert.isTrue(userList2.get(0).getName().equals("阿毅"),"data error!");
		//通過name模糊查詢資料
		List<AyUser> userList3 = ayUserService.findByNameLike("%毅%");
		System.out.println("findByNameLike() :" + userList3.size());
		Assert.isTrue(userList3.get(0).getName().equals("阿毅"),"data error!");
		//通過id列表查詢資料
		List<String> ids = new ArrayList<String>();
		ids.add("1");
		ids.add("2");
		List<AyUser> userList4 = ayUserService.findByIdIn(ids);
		System.out.println("findByIdIn() :" + userList4.size());
		//分頁查詢資料
		PageRequest pageRequest = new PageRequest(0,10);
		Page<AyUser> userList5 =  ayUserService.findAll(pageRequest);
	System.out.println("page findAll():" + userList5.getTotalPages() + "/" + userList5.getSize());
		//新增資料
		AyUser ayUser = new AyUser();
		ayUser.setId("3");
		ayUser.setName("test");
		ayUser.setPassword("123");
		ayUserService.save(ayUser);
		//刪除資料
		ayUserService.delete("3");
	}

Assert:新增Assert斷言,在軟體開發中是一種常用的除錯方式。從理論上來說,通過Assert斷言方式可以證明程式的正確性。在現在專案中被廣泛使用,這是大家需要掌握的基本知識。Assert提供了很多好用的方法,比如isNull,isTrue等。

3.3.2 測試

通過執行3.3.1節中的單元測試用例,我們可以在控制檯看到如下的列印資訊:

findAll() :2
findByName() :1
findByNameLike() :1
findByIdIn() :2
page findAll():1/10

通過上面的列印資訊,可以看出Spring Boot整合Spring Data JPA已經成功,同時代碼中的所有Assert斷言都全部通過,說明增刪改查分頁以及自定義查詢方法都可以正常執行。