開源實現
幾乎每個套用系統都需要通過訪問數據來完成工作。要想使用領域設計方法,你就需要為實體類定義和構建資源庫來實現領域對象的持久化。目前開發人員經常使用JPA來實現持久化庫。JPA讓持久化變得非常容易,但是仍然需要編寫很多模板代碼。Hades是一個開源庫,基於JPA和Spring構建,通過減少開發工作量顯著的改進了數據訪問層的實現。本文是Hades的嚮導性教程,會帶你走進Hades的世界,幫助你了解Hades在數據持久化方面能為你做些什麼,並簡要介紹了最新的2.0版本的新特性。
Hades的核心部分由通用的持久化庫實現構成,不僅提供了基本的CRUD(增刪改讀)方法,而且增加了一些通用場景功能的實現,類似分頁這樣的功能你就不用自己實現了。還包括查詢實體頁、動態增加排序定義等能力。
查詢
查詢是使用最多的數據訪問操作,如果你了解了Hades,就會意識到查詢作為Hades的核心功能之一,定義和執行查詢是多么容易,大大減少了工作量。一般完成一個查詢操作包括以下三個步驟:
增加特定實體的庫接口
增加查詢方法
微調查詢
首先你需要聲明一個繼承了GenericDao<Entity, Id>的接口,該類保證在Hades接口中定義的CRUD方法在你的接口中是有效的,如下:
public interface UserRepository extends GenericDao<User, Long> { … }
其次是根據業務需求為你的接口增加查詢方法:
List findByUsername(String username);
這又會引出問題,那就是如何實現查詢方法。如果你沒有增加任何附加的元數據,Hades會嘗試查找格式為{entity}. {method-name}的JPA查詢方法,如果有效的話就使用該方法。如果沒有找到任何可以解析的方法名,那就會創建一個查詢方法。因此,之前的例子就會產生這樣一樣查詢:select u from User u where u.username=?。查詢解析支持更多的關鍵字,你可以隨時在參考手冊文檔中找到它們。
如果想定義手工查詢,你可以使用Hades的@Query注釋功能,讓該查詢直接在方法中執行:
@Query("select from User u where u.lastname = ?");List someReallyStrangeName(String lastname);
這樣你可以自由的進行方法命名,既可以對查詢定義實現完全控制,又不必擔心那些查詢模板代碼。圍繞著執行查詢還有很多方式,例如使用分頁查詢、執行編輯查詢等。
引導Hades
到目前為止我們已經討論了如何創建帶有查詢方法的庫接口,接下來我們看一下如何使用這些接口。首先創建實體類的實例,然後根據該實例創建GenericDaoFactory的實例,通過工廠實例的getDao方法創建你需要的資源庫,如下代碼所示:
EntityManager em = … // Get access to EntityManagerGenericDaoFactory factory = GenericDaoFactory.create(em);UserRepository userRepository = factory.getDao(UserRepository.class);
由於資源庫經常通過依賴注入的方式被其客戶端持有,所以Hades提供了優雅的方式與與Spring套用集成。在Spring的套用中引導Hades非常簡單,只需使用Hades的命名空間,並聲明可以被匹配到的基礎包即可:
<hades:dao-config base-package="com.acme.*.repository" />
以上配置方式會選中所有繼承了GenericDao的資源庫接口,並為每個接口創建Spring的Bean。當然,命名空間支持更細粒度的配置,以實現精確控制。Hades的Eclipse外掛程式實現了與Spring IDE和SpringSource工具套件的無縫集成,這樣你就可以從其它Bean引用Hades的資源庫,並在你的工作空間把它們標記為Spring的Bean。想了解詳細信息請查看其參考手冊。
審計
在系統中記錄實體類的創建者、修改者和相關日期等信息是一個很常見的需求。Hades提供了監聽器EntityListener,可以幫你透明的實現這些功能。要啟用Hades的審計功能,只需要在orm.xml檔案中定義AuditingEntityListener即可:
<persistence-unit-metadata> <persistence-unit-defaults> <entity-listeners> <entity-listener class="org.synyx.hades.domain.auditing.support.AuditingEntityListener" /> </entity-listeners> </persistence-unit-defaults></persistence-unit-metadata>
並在Spring的配置中啟動審計功能,如下:
<hades:auditing auditor-aware-ref="auditorAware" />
auditorAware是引用的Spring Bean,需要實現AuditorAware接口,通常是實現針對當前用戶的安全查詢功能。如果你只想跟蹤創建和編輯日期,只需忽略該屬性即可。想了解更多審計特性,請參考相關文檔。
資源庫
Hades2.0 是基於JPA2.0以及相關的Hibernate、EclipseLink和OpenJPA版本構建的。基於該版本的CRUD事務操作使用非常簡單,開箱即用,對於相對簡單的場景基本不需要再進行事務性的封裝。未來結合資源庫接口可以讓事務管理變得更加簡單。
public interface UserRepository extends GenericDao<User, Long> { @Transactional(readOnly = true); List<User> findByUsername(); @Override @Transactional(readOnly = true, timeout = 60); List<User> readAll();}
正如你看到的,你可以簡單的通過為查詢方法增加注釋@Transactional的方式把它們加入事務。當然,也可以不用注釋的方式,基於XML的事務配置也能運行的很好。在GenericDao中的CRUD操作具備默認的事務性(對唯讀操作來說就是把readOnly設定為true)。如果你想為這些方法重新配置事務,簡單的為這些方法聲明自定義的@Transactional即可實現。想了解更多詳細信息,請參考事務相關的參考文檔。
規範
Hades 最新版本的一個非常酷的特性是基於GenericDao的擴展即可符合規範。這裡提到的規範指領域驅動設計的概念,DDD是由Eric Evans和Martin Fowler首次提出,從本質上捕捉實體的業務規則的一種開發方法。Hades提供的抽象方式可以很容易的基於JPA2.0的標準API實現領域驅動設計。在這裡我們假設你已經讀過上面提到的兩位作者寫的DDD相關的書籍。Hades提供了如下方式定義規範:
class BookSpecifications { public Specification<Book> hasAuthorWithFirstnameLike(final String firstname) { return new Specification<Book>() { public Predicate toPredicate(Root<Book> root, CriteriaQuery<?> cq, CriteriaBuilder cb) { return cb.like(root.join("author").get("firstname"), firstname); } } } public Specification<Book> hasGenre(final Genre genre) { return new Specification<Book>() { public Predicate toPredicate(Root<Book> root, CriteriaQuery<?> cq, CriteriaBuilder cb) { return cb.equal(root.get( "genre"), genre); } } }}
當然,這並不是最優雅的代碼片段(如果未來與Java 8里的單抽象方法(SAM)特性結果結合的話,效果會更好)。從積極的方面考慮,我們在資源庫層面獲得了執行規範的可能性:
bookRepository.readAll(hasAuthorWithFirstnameLike("Oliv*"));
好了,我們在聲明式查詢方面已經做了很多,不是嗎?規範的能力真正閃光的時候是把它們整合為新的規範的時候。Hades提供了規範助手類,可以進行任意的合併:
bookRepository.readAll(where(hasAuthorWithFirstnameLike( "Oliv*").or(hasGenre(Genres點IT)));
通過這種方式,擴展資源庫就變成了只是增加新的規範。少量的規範就可以幫助你使用類似DSL的柔性API查詢資源庫,而不是針對每次外部查詢請求都使用查詢方法訪問資源庫。想了解更多詳細信息,請參考相關文檔。
擴展
2.0 版本的另一部分內容是擴展模組,可以實現Hades與展現層技術框架(例如Spring的MVC)的無縫集成。它提供了屬性編輯器和Spring 3.0轉換器,可以透明的把實體類與MVC控制器方法的綁定在一起,同時MVC擴展還可以動態的從HTTP請求中提取分頁信息。一個展示用戶列表的頁面,在控制層的寫法就是這樣:
@Controllerclass UserController { @Autowired UserRepository userRepository; @RequestMapping("/users") public void showUsers(Pageable pageable, Model model) { model.addAttribute("users", userRepository.readAll(pageable)); } @RequestMapping("/users/{id}") public String showUser(@PathVariable("id") User user, Model model) { model.addAttribute("user", user); return"user"; }}
就像你看到的,showUsers方法不需要解析HttpServletRequest獲取分頁信息。另外請注意,在showUser(...)方法中綁定的id路徑變數的參數已經是實體類了。由於Spring MVC基礎框架的配置內容已經超出了本文要討論的範圍,如果想了解更多信息,請參考擴展模組配置信息的相關章節,其中提供了非常容易的設定樣例。
下一步
展望未來,2.1.x版本之後,Hades可能會成為Spring Data項目的一部分,其核心將作為實現其它數據存儲資源庫的實現基礎。Spring Data是SpringSource的項目,其目標是為新興的資料庫、特定的數據存儲提供Spring的方言支持,包括NoSQL資料庫。
總結
Hades 大大簡化了使用JPA執行數據層的訪問過程。你可以自由的進行精確的CRUD操作、執行查詢和規範等。Hades既可以獨立使用,可以方便的集成到 Spring中。除此之外,Eclipse的外掛程式Spring IDE/STS和附加的Spring Roo 都可以非常容易的創建資源庫。想了解更多信息,請參考項目網站。
關於作者
Oliver Gierke是VMware公司的SpringSource部門的高級顧問,是Hades開源項目的項目主管。大約在兩年前他在原公司Synyx啟動了該項目。