javascript
Spring Data JPA 从入门到精通~自定义实现Repository
EntityManager 的獲取方式
我們既然要自定義,首先講一下 EntityManager 的兩種獲取方式。
1. 通過 @PersistenceContext 注解。
通過將 @PersistenceContext 注解標(biāo)注在 EntityManager 類型的字段上,這樣得到的 EntityManager 就是容器管理的 EntityManager。由于是容器管理的,所以我們不需要也不應(yīng)該顯式關(guān)閉注入的 EntityManager 實(shí)例。
@Repository @Transactional(readOnly = true) public class UserRepositoryImpl implements UserRepositoryCustom {@PersistenceContext //獲得entityManager的實(shí)例EntityManager entityManager; }2. 繼承 SimpleJpaRepository 成為子類,實(shí)現(xiàn)構(gòu)造方法即可,這時(shí)候我們直接用父類里面的 EntityManager 即可。
public class BaseRepositoryCustom<T, ID> extends SimpleJpaRepository<T, ID> {public BaseRepositoryCustom(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager) {super(entityInformation, entityManager);}public BaseRepositoryCustom(Class<T> domainClass, EntityManager em) {super(domainClass, em);} }自定義 Repository 的兩種場景
我們自定義實(shí)現(xiàn) Repository,主要的應(yīng)用場景有兩種:
- 個別特殊化場景,私有的。
- 公用的通用的場景,替代默認(rèn)的 SimpleJpaRepository 的場景,架構(gòu)層面出發(fā)。
1. 自定義個別的特殊場景,私有的 Repository。
這種方法就是需要自己創(chuàng)建一個接口,和對應(yīng)的接口實(shí)現(xiàn)類,若需要用到特殊化的實(shí)現(xiàn)方法的話,***Respository 只需要繼承你自定義的接口即可,主要有兩種能力:
- 實(shí)現(xiàn)自定義接口
- 可以直接覆蓋 Spring Data JPA 給我們提供的默認(rèn) ***Respository 的接口里面的方法。
案例1:單個私有的 Repository 接口實(shí)現(xiàn)類
(1)創(chuàng)建自定義接口
/*** @author jack*/ public interface UserRepositoryCustom {/*** 自定義一個查詢方法,name的like查詢,此處僅僅是演示例子,實(shí)際中直接用QueryMethod即可* @param firstName* @return*/List<User> customerMethodNamesLike(String firstName); }(2)自定義存儲庫功能的實(shí)現(xiàn)
/*** 用@Repository 將此實(shí)現(xiàn)交個Spring bean加載* 咱們模仿SimpleJpaRepository 默認(rèn)將所有方法都開啟一個事務(wù)*/ @Repository @Transactional(readOnly = true) public class UserRepositoryCustomImpl implements UserRepositoryCustom {@PersistenceContextEntityManager entityManager;/*** 自定義一個查詢firstname的方法* @param firstName* @return*/@Overridepublic List<User> customerMethodNamesLike(String firstName) {Query query = entityManager.createNativeQuery("SELECT u.* FROM user as u " +"WHERE u.name LIKE ?", User.class);query.setParameter(1, firstName + "%");return query.getResultList();} }我們這里采用 entityManager,當(dāng)然了也不排除自己通過最底層的 JdbcTemplate 來自己實(shí)現(xiàn)邏輯。
(3)由于這個接口是為 User 單獨(dú)寫的,但是同時(shí)也可以繼承和 @Repository 的任何子類。
/*** 使用的時(shí)候直接繼承 UserRepositoryCustom接口即可*/ public interface UserRepository extends Repository<User, Long>,UserRepositoryCustom { }Controller 的調(diào)用方式如下:
/*** 調(diào)用我們自定義的實(shí)現(xiàn)方法** @return*/ @GetMapping(path = "/customer") @ResponseBody public Iterable<User> findCustomerMethodNamesLike() {return userRepository.customerMethodNamesLike("jack"); }(4)其實(shí)通過上述方法我們可以實(shí)現(xiàn)多個自定義接口:
//如:我們自定義了HumanCustomerRepository, ContactCustomerRepository兩個Repository interface UserRepository extends CrudRepository<User, Long>, HumanCustomerRepository, ContactCustomerRepository {// 用的時(shí)候只需要繼承多個自定義接口即可 }(5)覆蓋 JPA 里面的默認(rèn)實(shí)現(xiàn)方法
Spring Data JPA 的底層實(shí)現(xiàn)里面,自定義的 Repositories 的實(shí)現(xiàn)類和方法要高于它幫我們提供的 Repositories,所以當(dāng)我們有場景需要覆蓋默認(rèn)實(shí)現(xiàn)的時(shí)候其 demo 如下:
//假設(shè)我們要覆蓋默認(rèn)的save方法的邏輯 interface CustomizedSave<T> {<S extends T> S save(S entity); } class CustomizedSaveImpl<T> implements CustomizedSave<T> {public <S extends T> S save(S entity) {// Your custom implementation} } //用法保持不變,如下: interface UserRepository extends CrudRepository<User, Long>, CustomizedSave<User> { } //CustomizedSave通過泛化可以被多個Repository使用 interface PersonRepository extends CrudRepository<Person, Long>, CustomizedSave<Person> { }實(shí)際工作中應(yīng)用于邏輯刪除場景:
在實(shí)際工作的生產(chǎn)環(huán)境中,我們可能經(jīng)常會用到邏輯刪除,所以做法是一般自定義覆蓋 Data JPA 幫我們提供 remove 方法,然后實(shí)現(xiàn)邏輯刪除的邏輯即可。
2:公用的通用的場景,替代默認(rèn)的 SimpleJpaRepository 的場景,從架構(gòu)層面出發(fā)。
案例2:定義一個公用的 Repository 接口的實(shí)現(xiàn)類。
通過構(gòu)造方法獲得 EntityManager,需要用到 Java 的泛化技術(shù)。當(dāng)你想將一個方法添加到所有的存儲庫接口時(shí),上述方法是不可行的,要將自定義行為添加到所有存儲庫,首先添加一個中間接口來聲明共享行為。
(1)聲明定制共享行為的接口,用 @NoRepositoryBean:
//因?yàn)橐?#xff0c;所以必須要通用,不能失去本身的Spring Data JPA給我們提供的默認(rèn)方法,所有我們繼承相關(guān)的Repository類 @NoRepositoryBean public interface MyRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID> {void sharedCustomMethod(ID id); }(2)繼承 SimpleJpaRepository 擴(kuò)展自己的方法實(shí)現(xiàn)邏輯:
public class MyRepositoryImpl<T, ID extends Serializable>extends SimpleJpaRepository<T, ID> implements MyRepository<T, ID> {private final EntityManager entityManager;public MyRepositoryImpl(JpaEntityInformation entityInformation, EntityManager entityManager) {super(entityInformation, entityManager);// Keep the EntityManager around to used from the newly introduced methods.this.entityManager = entityManager;}public void sharedCustomMethod(ID id) {// 通過entityManager實(shí)現(xiàn)自己的額外方法的實(shí)現(xiàn)邏輯。這里不多說了} }注意:該類需要具有專門的存儲庫工廠實(shí)現(xiàn)使用超級類的構(gòu)造函數(shù),如果存儲庫基類有多個構(gòu)造函數(shù),則覆蓋一個 EntityInformation 加上特定于存儲的基礎(chǔ)架構(gòu)對象(例如,一個 EntityManager 或一個模板類),也可以重寫 SimpleJpaRepository 的任何邏輯。如邏輯刪除放在這里面實(shí)現(xiàn),就不要所有的 Repository 去關(guān)心實(shí)現(xiàn)哪個接口了。
(3)使用 JavaConfig 配置自定義 MyRepositoryImpl 作為其他接口的動態(tài)代理的實(shí)現(xiàn)基類。
具有全局的性質(zhì),即使沒有繼承它所有的動態(tài)代理類也會變成它。
@Configuration @EnableJpaRepositories(repositoryBaseClass = MyRepositoryImpl.class) class ApplicationConfiguration { … }(4)使用的時(shí)候就可以顯示的選擇用哪個接口,從而選擇性的暴露 SimpleJpaRepository 的實(shí)現(xiàn)方法。
現(xiàn)在,各個存儲庫接口將擴(kuò)展此中間接口,而不是擴(kuò)展 Repository 接口以包含聲明的功能。接下來,創(chuàng)建擴(kuò)展了持久性技術(shù)特定的存儲庫基類的中間接口的實(shí)現(xiàn)。然后,該類將用作存儲庫代理的自定義基類。
//如果你要使用你自定義的全局MyRepositoryImpl只需要繼承接口即可,如下: interface PersonRepository extends MyRepositoryImpl<Person, Long>{ }總結(jié)
以上是生活随笔為你收集整理的Spring Data JPA 从入门到精通~自定义实现Repository的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: Java技术回顾之JNDI--实例
- 下一篇: 有效的单元测试--总结
