JpaRepository如何實(shí)現(xiàn)增刪改查并進(jìn)行單元測(cè)試
JpaRepository增刪改查進(jìn)行單元測(cè)試
項(xiàng)目結(jié)構(gòu)

創(chuàng)建UserInfoDaoI.java文件,繼承JpaRepository。(不需要實(shí)現(xiàn)類)

根據(jù)條件查詢/刪除

更新

單元測(cè)試

SpringDataJPA的Repository理解
repository抽取擴(kuò)展理解
在SpringDataJPA中使用repository來進(jìn)行數(shù)據(jù)層操作(作用相當(dāng)于dao層),直接使用repository對(duì)象調(diào)用已經(jīng)實(shí)現(xiàn)好的數(shù)據(jù)層操作方法進(jìn)行CRUD、分頁、排序等操作,還可以在自定義repository接口中依據(jù)一定規(guī)則擴(kuò)展功能。在一個(gè)項(xiàng)目中,我們平常需要為每一個(gè)實(shí)體類都創(chuàng)建一個(gè)repository接口,我們自定義的repository接口都需要去繼承JpaRepository接口,以具有所有的數(shù)據(jù)層操作功能,但也僅僅限于簡(jiǎn)單查詢等操作。通常我們?yōu)榱四軐?shí)現(xiàn)復(fù)雜查詢操作,會(huì)繼續(xù)繼承JpaSpecificationExecutor接口,簡(jiǎn)單說就是建立一個(gè)規(guī)則來進(jìn)行查詢。
這樣雖然在很大程度上簡(jiǎn)化了開發(fā)代碼難度,但在為每一實(shí)體類創(chuàng)建對(duì)應(yīng)repository接口時(shí),發(fā)現(xiàn)存在大量的重復(fù)代碼,這時(shí)很容易的我們會(huì)想到抽取父類來簡(jiǎn)化一些公共代碼并順帶可以對(duì)子類進(jìn)行一些規(guī)范。雖然SpringDataJPA提供的功能已經(jīng)足夠強(qiáng)大了,但是依然還是不能滿足實(shí)際開發(fā)中的所有需求,所以我們想在抽取父類的同時(shí)實(shí)現(xiàn)對(duì)其功能的擴(kuò)展。在進(jìn)行這個(gè)操作之前我們需要了解一下整個(gè)repository的結(jié)構(gòu)。
應(yīng)該免不了有疑問,我們僅僅是定義了一個(gè)接口去實(shí)現(xiàn)了JpaRepository接口,怎么就能創(chuàng)建對(duì)象了,而且還能給我們實(shí)現(xiàn)一系列的數(shù)據(jù)層操作?這就要從為什么自定義repository接口要去繼承JpaRepository接口說起了:

通過它的結(jié)構(gòu)圖可以看出,JpaRepository的父類分別是PagingAndSortingRepository、CrudRepository和Repository,前兩個(gè)接口中前者定義了分頁和排序功能,后者看名字就可以知道里面全是CRUD操作,值得注意的是,它這保存和修改方法都是save。Repository接口呢沒有定義任何功能,它的作用就是標(biāo)識(shí)。這么說吧我們自定義的接口繼承JpaRepository接口就相當(dāng)于繼承了Repository接口,那么SpringDataJPA在掃描時(shí)只要掃描到我們某個(gè)接口實(shí)現(xiàn)了Repository接口,就為自動(dòng)的為其創(chuàng)建代理實(shí)現(xiàn)子類(通過AOP實(shí)現(xiàn))。但是為什么就只為我們自定義的repository接口創(chuàng)建了實(shí)現(xiàn)子類而沒有為PagingAndSortingRepository、CrudRepository、JpaRepository創(chuàng)建呢?這個(gè)同樣能從結(jié)構(gòu)圖中看出答案:它們都是打上了NoRepositoryBean注解的,凡是打上了這個(gè)注解的接口SPringDataJPA都不會(huì)為其創(chuàng)建實(shí)現(xiàn)子類。
這樣一來,我們自定義的接口繼承了以上三個(gè)接口后就相當(dāng)于“集百家之長(zhǎng)”了,擁有了它們所有功能,但是問題又來了,它們?cè)倥=K歸還是接口啊,又不能創(chuàng)建對(duì)象,是怎么操作的呢?

原來,SpringDataJPA自動(dòng)的為自動(dòng)創(chuàng)建的repository實(shí)現(xiàn)子類繼承了SimpleJpaRepository類,而SimpleJpaRepository又是SpringDataJPA的Repository默認(rèn)實(shí)現(xiàn)兩大方式之一,自然而然的自動(dòng)創(chuàng)建的repository代理子類也就具有了所有的功能。至此,SpringDataJPA中repository實(shí)現(xiàn)數(shù)據(jù)層操作的原理也大概講清楚了,接下來就說說擴(kuò)展了:雖然SPringDataJPA提供的功能已經(jīng)足夠強(qiáng)大了,但是依然還是不能滿足實(shí)際開發(fā)中的所有需求。所以說了這么久終于要說到重點(diǎn)了:抽取父類、擴(kuò)展功能。

通過上圖,大概也能看出結(jié)構(gòu)了,大概思想:在原本應(yīng)該繼承JpaRepository的實(shí)體類repository接口與JpaRepository之間增加了一層而已,讓BaseRepository繼承JpaSpecificationExecutor、JpaRepository,實(shí)體類repository接口繼承BaseRepository接口;這樣便能實(shí)現(xiàn)在擁有原有SpringDataJPA的Repository功能的情況下在BaseRepository擴(kuò)展其他功能,但在這需要注意:在SpringDataJPA中默認(rèn)會(huì)使自動(dòng)創(chuàng)建的repository代理實(shí)現(xiàn)子類(例:圖中的EmployeeRepositoryImpl)去繼承SimpleJpaRepository,很明顯我們并不能使用默認(rèn)的,因?yàn)檫@樣會(huì)使得我們的BaseRepository接口定義的一無是處,所以我們需要再創(chuàng)建一個(gè)自定義的BaseRepository實(shí)現(xiàn)BaseRepositoryImpl繼承SimpleJpaRepository,再修改配置使得SpringDataJPA自動(dòng)創(chuàng)建出的所有實(shí)體類repository的實(shí)現(xiàn)代理子類都去繼承我們的BaseRepositoryImpl,這樣我們便能徹底實(shí)現(xiàn)擴(kuò)展了。
前面說過SpringDataJPA會(huì)默認(rèn)的將自動(dòng)創(chuàng)建出的實(shí)現(xiàn)代理子類繼承SimpleJpaRepository類,所以我們需要修改SimpleJpaRepository為我們自定義的BaseRepositoryImpl類,SpringDataJPA是通過JpaRepositoryFactoryBean來實(shí)現(xiàn)創(chuàng)建并繼承過程的。我們只用自定義一個(gè)BaseRepositoryFactoryBean來繼承JpaRepositoryFactoryBean,接下來只用修改最終返回功能對(duì)象,和確定功能對(duì)象的類型即可。最后記得要在spring的配置文件中在SpringDataJPA的配置中添加修改創(chuàng)建對(duì)象的factoryBean為我們自定義的BaseRepositoryFactoryBean。
最后,記得修改各實(shí)體repository接口繼承我們定義的BaseRepository接口
接下來貼一貼代碼
接下來貼一貼代碼
BaseRepository
import com.xer.aisell.query.BaseQuery;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.NoRepositoryBean;
import java.io.Serializable;
import java.util.List;
/**
* 雖然jpadata提供的功能已經(jīng)足夠強(qiáng)大了,但是依然還是不能滿足實(shí)際開發(fā)中的所有需求
* 所以希望在具有它功能的前提下再拓展一些功能
* 之前是通過每個(gè)實(shí)體類的repository接口來直接繼承JpaRepository接口,現(xiàn)在我們可以在中間添加一個(gè)父類接口BaseRepository
* BaseRepository繼承JpaRepository,再由實(shí)體類repository來繼承BaseRepository
* 這樣就能夠?qū)崿F(xiàn)既實(shí)現(xiàn)了功能,又能隨時(shí)擴(kuò)展功能
* @param <T>
* @param <ID>
*/
@NoRepositoryBean
public interface BaseRepository<T,ID extends Serializable> extends JpaRepository<T,ID>,JpaSpecificationExecutor<T>{
//根據(jù)Query拿到分頁對(duì)象(分頁)
Page findPageByQuery(BaseQuery baseQuery);
//根據(jù)Query拿到對(duì)應(yīng)的所有數(shù)據(jù)(不分頁)
List<T> findByQuery(BaseQuery baseQuery);
//根據(jù)jpql與對(duì)應(yīng)的參數(shù)拿到數(shù)據(jù)
List findByJpql(String jpql,Object... values);
}
BaseRepositoryImpl
import com.xer.aisell.query.BaseQuery;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import java.io.Serializable;
import java.util.List;
public class BaseRepositoryImpl<T,ID extends Serializable> extends SimpleJpaRepository<T,ID> implements BaseRepository<T,ID> {
private final EntityManager entityManager;
public BaseRepositoryImpl(Class<T> domainClass, EntityManager em) {
super(domainClass, em);
this.entityManager = em;
}
@Override
/**
* 分頁
*/
public Page findPageByQuery(BaseQuery baseQuery) {
//拿到條件
Specification spec = baseQuery.createSpec();
//創(chuàng)建排序
Sort sort = baseQuery.getSort();
//分頁
Pageable page = new PageRequest(baseQuery.getJPACurrentPage(), baseQuery.getPageSize(), sort);
return super.findAll(spec,page);
}
/**
* 不分頁查詢
* @param baseQuery
* @return
*/
@Override
public List findByQuery(BaseQuery baseQuery) {
//獲取到條件
Specification spec = baseQuery.createSpec();
//排序
Sort sort = baseQuery.getSort();
return super.findAll(spec,sort);
}
/**
*
* 根據(jù)傳入JPQL查詢
* @param jpql
* @param values
* @return
*/
@Override
public List findByJpql(String jpql, Object... values) {
Query query = entityManager.createQuery(jpql);
//為傳入JPQL填充條件值
if (values != null) {
for (int i = 0;i < values.length;i++) {
query.setParameter(i+1,values[i]);
}
}
return query.getResultList();
}
}
EmployeeRepository接口
import com.xer.aisell.domain.Employee;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
import java.util.List;
/***
*用于數(shù)據(jù)庫(kù)操作
*/
public interface EmployeeRepository extends BaseRepository<Employee,Long> {
/**
* 可以自己擴(kuò)展
*/
List<Employee> findByUsernameLike(String username);
/**
* 使用@Query
* 實(shí)現(xiàn)自己寫JPQL查詢
*/
@Query("select o from Employee o where username like ?1")
List<Employee> findByUsername(String username);
/**
* 當(dāng)然也可以實(shí)現(xiàn)自己寫SQL
*/
@Query(nativeQuery = true,value = "SELECT COUNT(*) FROM employee")
Long getCount();
}
BaseRepositoryFactoryBean類(難)
import org.springframework.data.jpa.repository.support.JpaRepositoryFactory;
import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.core.RepositoryInformation;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
import javax.persistence.EntityManager;
import java.io.Serializable;
public class BaseRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extends Serializable> extends JpaRepositoryFactoryBean<T,S,ID> {
@Override
protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
return new MyRepositoryFactory<T,ID>(entityManager); //注:這里創(chuàng)建是我們的自定義類
}
//繼承JpaRepositoryFactory后,把返回的對(duì)象修改成我們自己的實(shí)現(xiàn)
private static class MyRepositoryFactory<T,ID extends Serializable> extends JpaRepositoryFactory {
private final EntityManager entityManager;
/**
* Creates a new {@link JpaRepositoryFactory}.
*
* @param entityManager must not be {@literal null}
*/
public MyRepositoryFactory(EntityManager entityManager) {
super(entityManager);
this.entityManager = entityManager;
}
//這里返回最后的功能對(duì)象
@Override
protected Object getTargetRepository(RepositoryInformation information) {
return new BaseRepositoryImpl<T,ID>((Class<T>)information.getDomainType(),entityManager);
}
//確定功能對(duì)象的類型
@Override
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
return BaseRepositoryImpl.class;
}
}
}
spring配置文件中關(guān)于SpringDataJPA的配置
<jpa:repositories base-package="com.xer.aisell.dao" entity-manager-factory-ref="entityManagerFactory"
transaction-manager-ref="transactionManager"
factory-class="com.xer.aisell.dao.BaseRepositoryFactoryBean"/>
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- SpringDataJpa:JpaRepository增刪改查操作
- 使用jpa原生sql@Query操作增刪改查
- Spring boot2+jpa+thymeleaf實(shí)現(xiàn)增刪改查
- SpringBoot+MySQL+Jpa實(shí)現(xiàn)對(duì)數(shù)據(jù)庫(kù)的增刪改查和分頁詳解
- spring-data-jpa實(shí)現(xiàn)增刪改查以及分頁操作方法
- JPA之使用JPQL語句進(jìn)行增刪改查
- SpringBoot JPA實(shí)現(xiàn)增刪改查、分頁、排序、事務(wù)操作等功能示例
- SpringDataJPA詳解增刪改查操作方法
相關(guān)文章
SpringBoot 配合 SpringSecurity 實(shí)現(xiàn)自動(dòng)登錄功能的代碼
這篇文章主要介紹了SpringBoot 配合 SpringSecurity 實(shí)現(xiàn)自動(dòng)登錄功能的代碼,代碼簡(jiǎn)單易懂,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-09-09
SpringBoot+WebSocket向前端推送消息的實(shí)現(xiàn)示例
WebSocket是一種在單個(gè)TCP連接上進(jìn)行全雙工通信的協(xié)議,允許服務(wù)器主動(dòng)向客戶端推送信息,同時(shí)也能從客戶端接收信息,本文主要介紹了SpringBoot+WebSocket向前端推送消息的實(shí)現(xiàn)示例,感興趣的可以了解一下2024-08-08
IDEA自動(dòng)清理類中未使用的import包的操作方法
在項(xiàng)目開發(fā)中,經(jīng)常會(huì)引入很多未使用的import包,這不僅增加了編譯時(shí)間,還會(huì)使代碼可讀性變差,設(shè)置IDEA自動(dòng)清理未使用的import包,可以提高代碼的可讀性,本文給大家介紹IDEA自動(dòng)清理類中未使用的import包的方法,感興趣的朋友一起看看吧2024-09-09
Java線程中的Thread.yield()詳細(xì)解析
這篇文章主要介紹了Java線程中的Thread.yield()詳細(xì)解析,yield()讓當(dāng)前線程從運(yùn)行狀態(tài)?轉(zhuǎn)為?就緒狀態(tài),以允許具有相同優(yōu)先級(jí)的其他線程獲得運(yùn)行機(jī)會(huì),需要的朋友可以參考下2023-11-11

