java jpa 规范_Java:在JPA中使用规范模式
java jpa 規范
本文是在Java中使用規范模式的簡介。 我們還將看到如何將經典規范與JPA Criteria查詢結合使用,以從關系數據庫中檢索對象。
在本文中,我們將使用以下Poll類作為創建規范的示例實體。 它表示具有開始和結束日期的民意調查。 在這兩個日期之間的時間中,用戶可以在不同的選擇之間進行投票。 到達結束日期之前,管理員還可以鎖定民意調查。 在這種情況下,將設置鎖定日期。
為了獲得更好的可讀性,我跳過了用于映射此示例中不需要的Joda DateTime實例和字段的getter,setter,JPA批注(例如民意調查中提出的問題)。
現在假設我們要實現兩個約束:
- 如果未鎖定且startDate <now <endDate,則輪詢當前正在運行
- 如果民意調查的投票數超過100并且未鎖定,則該投票很受歡迎
我們可以從向Poll添加適當的方法開始,例如:poll.isCurrentlyRunning()。 另外,我們可以使用類似pollService.isCurrentlyRunning(poll)的服務方法。 但是,我們還希望能夠查詢數據庫以獲取所有當前正在運行的民意調查。 因此,我們可以添加DAO或存儲庫方法,例如pollRepository.findAllCurrentlyRunningPolls()。
如果采用這種方式,我們將在兩個不同的位置兩次實現isCurrentlyRunning約束。 如果我們要結合約束,事情就會變得更糟。 如果我們想查詢數據庫以獲取當前正在運行的所有流行民意測驗的列表怎么辦?
這是規范模式派上用場的地方。 使用規范模式時,我們將業務規則移到稱為規范的額外類中。
首先,我們創建一個簡單的界面和一個抽象類:
public?interface?Specification<T>?{??boolean?isSatisfiedBy(T?t);??Predicate?toPredicate(Root<T>?root,?CriteriaBuilder?cb);Class<T>?getType(); }abstract?public?class?AbstractSpecification<T>?implements?Specification<T>?{@Overridepublic?boolean?isSatisfiedBy(T?t)?{throw?new?NotImplementedException();}??@Overridepublic?Predicate?toPredicate(Root<T>?poll,?CriteriaBuilder?cb)?{throw?new?NotImplementedException();}@Overridepublic?Class<T>?getType()?{ParameterizedType?type?=?(ParameterizedType)?this.getClass().getGenericSuperclass();return (Class<T>) type.getActualTypeArguments()[0];} }請暫時忽略帶有神秘的getType()方法的AbstractSpecification <T>類(我們稍后再介紹)。
規范的中心部分是isSatisfiedBy()方法,該方法用于檢查對象是否滿足規范。 toPredicate()是我們在本示例中使用的另一種方法,用于以javax.persistence.criteria.Predicate實例的形式返回約束,該約束可用于查詢數據庫。
對于每個約束,我們創建一個新的規范類,該類擴展AbstractSpecification <T>并實現isSatisfiedBy()和toPredicate()。
檢查輪詢是否正在運行的規范實現如下所示:
public?class?IsCurrentlyRunning?extends?AbstractSpecification<Poll>?{@Overridepublic?boolean?isSatisfiedBy(Poll?poll)?{return?poll.getStartDate().isBeforeNow()?&&?poll.getEndDate().isAfterNow()?&&?poll.getLockDate()?==?null;}@Overridepublic?Predicate?toPredicate(Root<Poll>?poll,?CriteriaBuilder?cb)?{DateTime?now?=?new?DateTime();return?cb.and(cb.lessThan(poll.get(Poll_.startDate),?now),cb.greaterThan(poll.get(Poll_.endDate),?now),cb.isNull(poll.get(Poll_.lockDate)));} }在isSatisfiedBy()中,我們檢查傳遞的對象是否與約束匹配。 在toPredicate()中,我們使用JPA的CriteriaBuilder構造謂詞。 稍后,我們將使用結果謂詞實例來構建用于查詢數據庫的CriteriaQuery。
用于檢查民意調查是否受歡迎的規范看起來類似:
public?class?IsPopular?extends?AbstractSpecification<Poll>?{@Overridepublic?boolean?isSatisfiedBy(Poll?poll)?{return?poll.getLockDate()?==?null?&&?poll.getVotes().size()?> 100;}??@Overridepublic?Predicate?toPredicate(Root<Poll>?poll,?CriteriaBuilder?cb)?{return?cb.and(cb.isNull(poll.get(Poll_.lockDate)),cb.greaterThan(cb.size(poll.get(Poll_.votes)),?5));} }如果現在要測試Poll實例是否符合以下約束之一,則可以使用我們新創建的規范:
boolean isPopular = new?IsPopular().isSatisfiedBy(poll); boolean isCurrentlyRunning = new?IsCurrentlyRunning().isSatisfiedBy(poll);為了查詢數據庫,我們需要擴展DAO /存儲庫以支持規范。 看起來可能如下所示:
public?class?PollRepository?{private?EntityManager?entityManager?=?...public?<T>?List<T>?findAllBySpecification(Specification<T>?specification)?{CriteriaBuilder?criteriaBuilder?=?entityManager.getCriteriaBuilder();//?use?specification.getType()?to?create?a?Root<T>?instanceCriteriaQuery<T>?criteriaQuery?=?criteriaBuilder.createQuery(specification.getType());Root<T>?root?=?criteriaQuery.from(specification.getType());//?get?predicate?from?specificationPredicate?predicate?=?specification.toPredicate(root,?criteriaBuilder);// set predicate and execute querycriteriaQuery.where(predicate);return?entityManager.createQuery(criteriaQuery).getResultList();} }在這里,我們最終使用在AbstractSpecification <T>中實現的getType()方法來創建CriteriaQuery <T>和Root <T>實例。 getType()返回由子類定義的AbstractSpecification <T>實例的泛型類型。 對于IsPopular和IsCurrentlyRunning,它返回Poll類。 如果沒有getType(),我們將不得不在我們創建的每個規范的toPredicate()內創建CriteriaQuery <T>和Root <T>實例。 因此,減少規范內的樣板代碼只是一個小幫手。 如果您想出更好的方法,請隨時用自己的實現替換它。
現在,我們可以使用我們的存儲庫來查詢數據庫以查找與特定規范匹配的民意測驗:
List<Poll>?popularPolls?=?pollRepository.findAllBySpecification(new?IsPopular()); List<Poll>?currentlyRunningPolls?=?pollRepository.findAllBySpecification(new?IsCurrentlyRunning());在這一點上,規范是唯一包含約束定義的組件。 我們可以使用它來查詢數據庫或檢查對象是否滿足必需的規則。
但是,仍然存在一個問題:我們如何結合兩個或更多個約束? 例如,我們想查詢數據庫以獲取所有仍在運行的流行民意調查。
答案是復合設計模式的一種變化,稱為復合規格。 使用復合規范,我們可以以不同方式組合規范。
要查詢數據庫中所有正在運行和流行的池,我們需要使用邏輯和操作將isCurrentlyRunning與isPopular規范結合在一起。 讓我們為此創建另一個規范。 我們將其命名為AndSpecification:
public?class?AndSpecification<T>?extends?AbstractSpecification<T>?{private?Specification<T>?first;private?Specification<T>?second;public?AndSpecification(Specification<T>?first,?Specification<T>?second)?{this.first?=?first;this.second?=?second;}@Overridepublic?boolean?isSatisfiedBy(T?t)?{return?first.isSatisfiedBy(t)?&&?second.isSatisfiedBy(t);}@Overridepublic?Predicate?toPredicate(Root<T>?root,?CriteriaBuilder?cb)?{return?cb.and(first.toPredicate(root,?cb),?second.toPredicate(root,?cb));}@Overridepublic?Class<T>?getType()?{return?first.getType();} }AndSpecification是從其他兩個規范中創建的。 在isSatisfiedBy()和toPredicate()中,我們通過邏輯和運算返回兩個規范的結果。
我們可以像這樣使用我們的新規范:
Specification<Poll>?popularAndRunning?=?new?AndSpecification<>(new?IsPopular(),?new?IsCurrentlyRunning()); List<Poll>?polls?=?myRepository.findAllBySpecification(popularAndRunning);為了提高可讀性,我們可以在規范接口中添加and()方法:
public?interface?Specification<T>?{Specification<T>?and(Specification<T>?other);//?other?methods }并在我們的抽象實現中實現它:
abstract?public?class?AbstractSpecification<T>?implements?Specification<T>?{@Overridepublic?Specification<T>?and(Specification<T>?other)?{return?new?AndSpecification<>(this,?other);}//?other?methods }現在,我們可以使用and()方法鏈接多個規范:
Specification<Poll>?popularAndRunning?=?new?IsPopular().and(new?IsCurrentlyRunning()); boolean?isPopularAndRunning?=?popularAndRunning.isSatisfiedBy(poll); List<Poll>?polls?=?myRepository.findAllBySpecification(popularAndRunning);在需要時,我們可以輕松地通過其他復合規范(例如OrSpecification或NotSpecification)進一步擴展該規范。
結論
使用規范模式時,我們將業務規則移到單獨的規范類中。 通過使用復合規范,可以輕松組合這些規范類。 通常,規范可以提高可重用性和可維護性。 另外,規格可以輕松進行單元測試。 有關規范模式的更多詳細信息,我推薦Eric Evans和Martin Fowler 撰寫的這篇文章 。
- 您可以在GitHub上找到此示例項目的源代碼。
翻譯自: https://www.javacodegeeks.com/2014/01/java-using-the-specification-pattern-with-jpa.html
java jpa 規范
總結
以上是生活随笔為你收集整理的java jpa 规范_Java:在JPA中使用规范模式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何在ADF中将参数传递给ActionL
- 下一篇: 腾讯ddos防御(腾讯ddos高防包)