jpa原生query_資料庫持久化之JPA 和 Mybatis 如何進行技術選型?
技術標籤:jpa原生query
來源:https://mp.weixin.qq.com/s/yHQJff9cMXYKy_wK68qfCA
在我們平時的專案中,大家都知道可以使用 JPA 或者 Mybatis 作為 ORM 層。對 JPA 和 Mybatis 如何進行技術選型?
下面看看大精華總結如下:
最佳回答
首先表達個人觀點,JPA必然是首選的。
個人認為僅僅討論兩者使用起來有何區別,何者更加方便,不足以真正的比較這兩個框架。要評判出更加優秀的方案,我覺得可以從軟體設計的角度來評判。個人對 mybatis 並不熟悉,但 JPA 規範和 springdata 的實現,設計理念絕對是超前的。軟體開發複雜性的一個解決手段是遵循 DDD(DDD 只是一種手段,但不是唯一手段),而我著重幾點來聊聊 JPA 的設計中是如何體現領域驅動設計思想的,拋磚引玉。
聚合根和值物件
領域驅動設計中有兩個廣為大家熟知的概念,entity(實體)和 value object(值物件)。entity 的特點是具有生命週期的,有標識的,而值物件是起到一個修飾的作用,其具有不可變性,無標識。在 JPA中 ,需要為資料庫的實體類新增 @Entity 註解,相信大家也注意到了,這並不是巧合。
如上述的程式碼,Order 便是 DDD 中的實體,而 CustomerVo,OrderItem 則是值物件。程式設計者無需關心資料庫如何對映這些欄位,因為在 DDD 中,需要做的工作是領域建模,而不是資料建模。實體和值物件的意義不在此展開討論,但通過此可以初見端倪,JPA 的內涵絕不僅僅是一個 ORM 框架。
倉儲
Repository 模式是領域驅動設計中另一個經典的模式。在早期,我們常常將資料訪問層命名為:DAO,而在 SpringData JPA 中,其稱之為 Repository(倉儲),這也不是巧合,而是設計者有意為之。
熟悉 SpringData JPA 的朋友都知道當一個介面繼承 JpaRepository 介面之後便自動具備了 一系列常用的資料操作方法,findAll, findOne ,save等。
public interface OrderRepository extends JpaRepository{}
那麼倉儲和DAO到底有什麼區別呢?這就要提到一些遺留問題,以及一些軟體設計方面的因素。在這次SpringForAll 的議題中我能夠預想到有很多會強調 SpringData JPA 具有方便可擴充套件的 API,像下面這樣:
public interface OrderRepository extends JpaRepository{ findByOrderNoAndXxxx(String orderNo,Xxx xx); @Transactional @Modifying(clearAutomatically = true) @Query("update t_order set order_status =?1 where id=?2") int updateOrderStatusById(String orderStatus, String id);}
但我要強調的是,這是 SpringData JPA 的妥協,其支援這一特性,並不代表其建議使用。因為這並不符合領域驅動設計的理念。注意對比,SpringData JPA 的設計理念是將 Repository 作為資料倉庫,而不是一系列資料庫指令碼的集合,findByOrderNoAndXxxx 方法可以由下面一節要提到的JpaSpecificationExecutor代替,而 updateOrderStatusById 方法則可以由 findOne + save 代替,不要覺得這變得複雜了,試想一下真正的業務場景,修改操作一般不會只涉及一個欄位的修改, findOne + save 可以幫助你完成更加複雜業務操作,而不必關心我們該如何編寫 SQL 語句,真正做到了面向領域開發,而不是面向資料庫 SQL 開發,面向物件的擁躉者也必然會覺得,這更加的 OO。
Specification
上面提到 SpringData JPA 可以藉助 Specification 模式代替複雜的 findByOrderNoAndXxxx 一類 SQL 指令碼的查詢。試想一下,業務不停在變,你怎麼知道將來的查詢會不會多一個條件 變成 findByOrderNoAndXxxxAndXxxxAndXxxx.... 。SpringData JPA 為了實現領域驅動設計中的 Specification 模式,提供了一些列的 Specification 介面,其中最常用的便是 :JpaSpecificationExecutor
public interface OrderRepository extends JpaRepository,JpaSpecificationExecutor{}
使用 SpringData JPA 構建複雜查詢(join操作,聚集操作等等)都是依賴於 JpaSpecificationExecutor 構建的 Specification 。例子就不介紹,有點長。
請注意,上述程式碼並不是一個例子,在真正遵循 DDD 設計規範的系統中,OrderRepository 介面中就應該是乾乾淨淨的,沒有任何程式碼,只需要繼承 JpaRepository (負責基礎CRUD)以及 JpaSpecificationExecutor (負責Specification 查詢)即可。當然, SpringData JPA 也提供了其他一系列的介面,根據特定業務場景繼承即可。
樂觀鎖
為了解決資料併發問題,JPA 中提供了 @Version ,一般在 Entity 中 新增一個 Long version 欄位,配合 @Version 註解,SpringData JPA 也考慮到了這一點。這一點側面體現出,JPA 設計的理念和 SpringData 作為一個工程解決方案的雙劍合璧,造就出了一個偉大的設計方案。
複雜的多表查詢
很多人青睞 Mybatis ,原因是其提供了便利的 SQL 操作,自由度高,封裝性好……SpringData JPA對複雜 SQL 的支援不好,沒有實體關聯的兩個表要做 join ,的確要花不少功夫。但 SpringData JPA 並不把這個當做一個問題。為什麼?因為現代微服務的架構,各個服務之間的資料庫是隔離的,跨越很多張表的 join 操作本就不應該交給單一的業務資料庫去完成。解決方案是:使用 elasticSearch做檢視查詢 或者 mongodb 一類的Nosql 去完成。問題本不是問題。
總結
真正走進 JPA,真正走進 SpringData 會發現,我們並不是在解決一個數據庫查詢問題,並不是在使用一個 ORM 框架,而是真正地在實踐領域驅動設計。
(再次補充:DDD 只是一種手段,但不是唯一手段)
第二名回答
lexburne 兄說的也很不錯了,不過我還想在補充2點,來消除大家對使用spring data jpa 的誤解 spring data jpa 的好處我相信大家都瞭解,就是開發速度很快,很方便,大家不願意使用spring data jpa 的地方通常是因為sql 不是自己寫的,不可控,複雜查詢不好搞,那麼下面我要說的就是其實對於這種需求,spring data jpa 是完全支援的!!
第一種方式:@query 註解指定nativeQuery,這樣就可以使用原生sql查詢了,示例程式碼來自官方文件:
public interface UserRepository extends JpaRepository { @Query(value = "SELECT * FROM USERS WHERE EMAIL_ADDRESS = ?1