數據庫持久化之JPA 和 Mybatis 如何進行技術選型?

來源: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 註解,相信大家也注意到了,這並不是巧合。

數據庫持久化之JPA 和 Mybatis 如何進行技術選型?

如上述的代碼,Order 便是 DDD 中的實體,而 CustomerVo,OrderItem 則是值對象。程序設計者無需關心數據庫如何映射這些字段,因為在 DDD 中,需要做的工作是領域建模,而不是數據建模。實體和值對象的意義不在此展開討論,但通過此可以初見端倪,JPA 的內涵絕不僅僅是一個 ORM 框架。

倉儲

Repository 模式是領域驅動設計中另一個經典的模式。在早期,我們常常將數據訪問層命名為:DAO,而在 SpringData JPA 中,其稱之為 Repository(倉儲),這也不是巧合,而是設計者有意為之。

熟悉 SpringData JPA 的朋友都知道當一個接口繼承 JpaRepository 接口之後便自動具備了 一系列常用的數據操作方法,findAll, findOne ,save等。

public interface OrderRepository extends JpaRepository<order>{
}
/<order>

那麼倉儲和DAO到底有什麼區別呢?這就要提到一些遺留問題,以及一些軟件設計方面的因素。在這次SpringForAll 的議題中我能夠預想到有很多會強調 SpringData JPA 具有方便可擴展的 API,像下面這樣:

public interface OrderRepository extends JpaRepository<order>{
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);
}
/<order>

但我要強調的是,這是 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<order>,JpaSpecificationExecutor<order>{
}
/<order>/<order>

使用 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<user> {
@Query(value = "SELECT * FROM USERS WHERE EMAIL_ADDRESS = ?1", nativeQuery = true)
User findByEmailAddress(String emailAddress);
}
/<user>

2.如果單靠sql搞不定怎麼辦?必須要寫邏輯怎麼辦?可以使用官方文檔3.6.1 節:Customizing individual repositories 提供的功能去實現,先看官方文檔的代碼:

interface CustomizedUserRepository {
void someCustomMethod(User user);
}
class CustomizedUserRepositoryImpl implements CustomizedUserRepository {
public void someCustomMethod(User user) {
// Your custom implementation
}
}
interface UserRepository extends CrudRepository<user>, CustomizedUserRepository {
// Declare query methods here
}
/<user>

我來解釋下上面的代碼,如果搞不定的查詢方法,可以自定義接口,例如CustomizedUserRepository ,和他的實現了類,然後在這個實現類裡用你自己喜歡的dao 框架,比如說mybatis ,jdbcTemplate ,都隨意,最後在用UserRepository 去繼承CustomizedUserRepository接口,就實現了和其他dao 框架的組合使用!!

那麼下面我在總結1下,有了上面介紹的2種功能,你還在擔心,使用spring data jpa 會有侷限麼,他只會加速你的開發速度,並允許你組合使用其他框架,只有好處,沒有壞處。。

最後再說1點,我最近在看es ,然後看了下spring data es 的文檔,大概掃了1下,我發現,學會spring data 其中某1個系列以後,在看其他的,我發現我都不用花時間學。。直接就可以用,對就是這麼神奇~~

第三

工作以來一直是使用 hibernate 和 mybatis,總結下來一般傳統公司、個人開發(可能只是我)喜歡用jpa,互聯網公司更青睞於 mybatis

原因:mybatis 更加靈活。開發迭代模式決定的,傳統公司需求迭代速度慢,項目改動小,hibernate可以幫他們做到一勞永逸;而互聯網公司追求快速迭代,需求快速變更,靈活的 mybatis 修改起來更加方便,而且一般每一次的改動不會帶來性能上的下降,hibernate經常因為添加關聯關係或者開發者不瞭解優化導致項目越來越糟糕(本來開始也是性能很好的)

  • mybatis官方文檔就說了他是一個半自動化的持久層框架,相對於全自動化的 hibernate 他更加的靈活、可控
  • mybatis 的學習成本低於 hibernate。hibernate 使用需要對他有深入的理解,尤其是緩存方面,作為一個持久層框架,性能依然是第一位的。

hibernate 它有著三級緩存,一級緩存是默認開啟的,二級緩存需要手動開啟以及配置優化,三級緩存可以整合業界流行的緩存技術 redis,ecache 等等一起去實現

hibernate 使用中的優化點:

  • 緩存的優化
  • 關聯查詢的懶加載(在開發中,還是不建議過多使用外鍵去關聯操作)

jpa(Java Persistence API) 與 hibernate 的關係:

  • Jpa是一種規範,hibernate 也是遵從他的規範的。
  • springDataJpa 是對 repository 的封裝,簡化了 repository 的操作

第四

  • 數據分析型的OLAP應用適合用MyBatis,事務處理型OLTP應用適合用JPA。
  • 越是複雜的業務,越需要領域建模,建模用JPA實現最方便靈活。但是JPA想用好,門檻比較高,不懂DDD的話,就會淪為增刪改查了。
  • 複雜的查詢應該是通過CQRS模式,通過異步隊列建立合適查詢的視圖,通過視圖避免複雜的Join,而不是直接查詢領域模型。
  • 從目前的趨勢來看OLAP交給NoSQL數據庫可能更合適

第五

使用了一段時間jpa,而mybatis是之前一直在用的,不說區別是啥,因為有很多人比較這兩個框架了!

  • 從國內開源的應用框架來看,國內使用jpa做orm的人還是比較少,如果換成hibernate還會多一些,所以面臨的風險可能就是你會用,和你合作的人不一定會用,如果要多方協作,肯定要考慮這個問題!
  • 靈活性方面,jpa更靈活,包括基本的增刪改查、數據關係以及數據庫的切換上都比mybatis靈活,但是jpa門檻較高,另外就是更新數據需要先將數據查出來才能進行更新,數據量大的時候,jpa效率會低一些,這時候需要做一些額外的工作去處理!
  • 現在結合Springboot有Springdata jpa給到,很多東西都簡化了,感興趣並且有能力可以考慮在公司內部和圈子裡推廣

第六

  1. 相對來說,jpa的學習成本比mybatis略高
  2. 公司業務需求頻繁變更導致表結構複雜,此處使用mybatis比jpa更靈活
  3. 就方言來講,一般公司選定數據庫後再變更微乎其微,所以此處方言的優勢可以忽略


分享到:


相關文章: