第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斷言都全部通過,說明增刪改查分頁以及自定義查詢方法都可以正常執行。