JpaRepository如何實現(xiàn)增刪改查并進行單元測試
JpaRepository增刪改查進行單元測試
項目結構
創(chuàng)建UserInfoDaoI.java文件,繼承JpaRepository。(不需要實現(xiàn)類)
根據(jù)條件查詢/刪除
更新
單元測試
SpringDataJPA的Repository理解
repository抽取擴展理解
在SpringDataJPA中使用repository來進行數(shù)據(jù)層操作(作用相當于dao層),直接使用repository對象調用已經(jīng)實現(xiàn)好的數(shù)據(jù)層操作方法進行CRUD、分頁、排序等操作,還可以在自定義repository接口中依據(jù)一定規(guī)則擴展功能。在一個項目中,我們平常需要為每一個實體類都創(chuàng)建一個repository接口,我們自定義的repository接口都需要去繼承JpaRepository接口,以具有所有的數(shù)據(jù)層操作功能,但也僅僅限于簡單查詢等操作。通常我們?yōu)榱四軐崿F(xiàn)復雜查詢操作,會繼續(xù)繼承JpaSpecificationExecutor接口,簡單說就是建立一個規(guī)則來進行查詢。
這樣雖然在很大程度上簡化了開發(fā)代碼難度,但在為每一實體類創(chuàng)建對應repository接口時,發(fā)現(xiàn)存在大量的重復代碼,這時很容易的我們會想到抽取父類來簡化一些公共代碼并順帶可以對子類進行一些規(guī)范。雖然SpringDataJPA提供的功能已經(jīng)足夠強大了,但是依然還是不能滿足實際開發(fā)中的所有需求,所以我們想在抽取父類的同時實現(xiàn)對其功能的擴展。在進行這個操作之前我們需要了解一下整個repository的結構。
應該免不了有疑問,我們僅僅是定義了一個接口去實現(xiàn)了JpaRepository接口,怎么就能創(chuàng)建對象了,而且還能給我們實現(xiàn)一系列的數(shù)據(jù)層操作?這就要從為什么自定義repository接口要去繼承JpaRepository接口說起了:
通過它的結構圖可以看出,JpaRepository的父類分別是PagingAndSortingRepository、CrudRepository和Repository,前兩個接口中前者定義了分頁和排序功能,后者看名字就可以知道里面全是CRUD操作,值得注意的是,它這保存和修改方法都是save。Repository接口呢沒有定義任何功能,它的作用就是標識。這么說吧我們自定義的接口繼承JpaRepository接口就相當于繼承了Repository接口,那么SpringDataJPA在掃描時只要掃描到我們某個接口實現(xiàn)了Repository接口,就為自動的為其創(chuàng)建代理實現(xiàn)子類(通過AOP實現(xiàn))。但是為什么就只為我們自定義的repository接口創(chuàng)建了實現(xiàn)子類而沒有為PagingAndSortingRepository、CrudRepository、JpaRepository創(chuàng)建呢?這個同樣能從結構圖中看出答案:它們都是打上了NoRepositoryBean注解的,凡是打上了這個注解的接口SPringDataJPA都不會為其創(chuàng)建實現(xiàn)子類。
這樣一來,我們自定義的接口繼承了以上三個接口后就相當于“集百家之長”了,擁有了它們所有功能,但是問題又來了,它們再牛終歸還是接口啊,又不能創(chuàng)建對象,是怎么操作的呢?
原來,SpringDataJPA自動的為自動創(chuàng)建的repository實現(xiàn)子類繼承了SimpleJpaRepository類,而SimpleJpaRepository又是SpringDataJPA的Repository默認實現(xiàn)兩大方式之一,自然而然的自動創(chuàng)建的repository代理子類也就具有了所有的功能。至此,SpringDataJPA中repository實現(xiàn)數(shù)據(jù)層操作的原理也大概講清楚了,接下來就說說擴展了:雖然SPringDataJPA提供的功能已經(jīng)足夠強大了,但是依然還是不能滿足實際開發(fā)中的所有需求。所以說了這么久終于要說到重點了:抽取父類、擴展功能。
通過上圖,大概也能看出結構了,大概思想:在原本應該繼承JpaRepository的實體類repository接口與JpaRepository之間增加了一層而已,讓BaseRepository繼承JpaSpecificationExecutor、JpaRepository,實體類repository接口繼承BaseRepository接口;這樣便能實現(xiàn)在擁有原有SpringDataJPA的Repository功能的情況下在BaseRepository擴展其他功能,但在這需要注意:在SpringDataJPA中默認會使自動創(chuàng)建的repository代理實現(xiàn)子類(例:圖中的EmployeeRepositoryImpl)去繼承SimpleJpaRepository,很明顯我們并不能使用默認的,因為這樣會使得我們的BaseRepository接口定義的一無是處,所以我們需要再創(chuàng)建一個自定義的BaseRepository實現(xiàn)BaseRepositoryImpl繼承SimpleJpaRepository,再修改配置使得SpringDataJPA自動創(chuàng)建出的所有實體類repository的實現(xiàn)代理子類都去繼承我們的BaseRepositoryImpl,這樣我們便能徹底實現(xiàn)擴展了。
前面說過SpringDataJPA會默認的將自動創(chuàng)建出的實現(xiàn)代理子類繼承SimpleJpaRepository類,所以我們需要修改SimpleJpaRepository為我們自定義的BaseRepositoryImpl類,SpringDataJPA是通過JpaRepositoryFactoryBean來實現(xiàn)創(chuàng)建并繼承過程的。我們只用自定義一個BaseRepositoryFactoryBean來繼承JpaRepositoryFactoryBean,接下來只用修改最終返回功能對象,和確定功能對象的類型即可。最后記得要在spring的配置文件中在SpringDataJPA的配置中添加修改創(chuàng)建對象的factoryBean為我們自定義的BaseRepositoryFactoryBean。
最后,記得修改各實體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)足夠強大了,但是依然還是不能滿足實際開發(fā)中的所有需求 * 所以希望在具有它功能的前提下再拓展一些功能 * 之前是通過每個實體類的repository接口來直接繼承JpaRepository接口,現(xiàn)在我們可以在中間添加一個父類接口BaseRepository * BaseRepository繼承JpaRepository,再由實體類repository來繼承BaseRepository * 這樣就能夠實現(xiàn)既實現(xiàn)了功能,又能隨時擴展功能 * @param <T> * @param <ID> */ @NoRepositoryBean public interface BaseRepository<T,ID extends Serializable> extends JpaRepository<T,ID>,JpaSpecificationExecutor<T>{ //根據(jù)Query拿到分頁對象(分頁) Page findPageByQuery(BaseQuery baseQuery); //根據(jù)Query拿到對應的所有數(shù)據(jù)(不分頁) List<T> findByQuery(BaseQuery baseQuery); //根據(jù)jpql與對應的參數(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ù)庫操作 */ public interface EmployeeRepository extends BaseRepository<Employee,Long> { /** * 可以自己擴展 */ List<Employee> findByUsernameLike(String username); /** * 使用@Query * 實現(xiàn)自己寫JPQL查詢 */ @Query("select o from Employee o where username like ?1") List<Employee> findByUsername(String username); /** * 當然也可以實現(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后,把返回的對象修改成我們自己的實現(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; } //這里返回最后的功能對象 @Override protected Object getTargetRepository(RepositoryInformation information) { return new BaseRepositoryImpl<T,ID>((Class<T>)information.getDomainType(),entityManager); } //確定功能對象的類型 @Override protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) { return BaseRepositoryImpl.class; } } }
spring配置文件中關于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"/>
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
Java如何將int型數(shù)組轉為String型數(shù)組
這篇文章主要介紹了Java如何將int型數(shù)組轉為String型數(shù)組,本文給大家分享具體實現(xiàn)思路結合實例代碼給大家介紹的非常詳細,感興趣的朋友跟隨小編一起看看吧2024-03-03分布式開發(fā)醫(yī)療掛號系統(tǒng)數(shù)據(jù)字典模塊前后端實現(xiàn)
這篇文章主要為大家介紹了分布式開發(fā)醫(yī)療掛號系統(tǒng)數(shù)據(jù)字典模塊前后端實現(xiàn),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-04-04SpringBoot實現(xiàn)無感刷新Token的項目實踐
token刷新是前端安全中必要的一部分,本文就來介紹一下SpringBoot實現(xiàn)無感刷新Token的項目實踐,具有一定的參考價值,感興趣的可以了解一下2024-03-03