java數據庫持久層框架基礎:爲什麼不是JPA?

mybatis和JPA

    關於java持久層框架的故事的起源,還是要歸結到java語言JDBC(Java Database Connectivity)設計上的不夠成熟。jdbc底層使用sql操作關係型數據庫,這種方式本身和java“一切皆對象”的設計理念格格不入。如果書寫的sql語句和某特定類型的數據庫強相關,也會導致應用的移植性下降。
    這個時候JPA和Mybatis應運而生。
    嚴格來講,JPA(這裏並不是spring data jpa)和Mybatis並不能作爲ORM框架放在一起討論。JPA全稱是Java Persistence API,是sun公司定義的一套 java ORM框架標準,但是流氓的是sun公司自己並沒有實現這套標準。目前大家比較熟知的JBoss公司維護的Hibernate和spring團隊維護的基於Hibernate的spring data JPA,就是基於JPA標準的實現。
    Herbernate和spirng data JPA是全自動的ORM框架,可以自動生成sql語句,從而實現應用層和數據庫持久層的隔離。而Mybatis是一種半自動的ORM框架,雖然目前可以使用Mybatis plus,極大地簡化了開發,但有時候還是需要開發者自己手寫sql。
    關於JPA和Mybatis之爭,可以參考
2018年JVM生態報告中關於ORM框架使用佔比的數據。
java數據庫持久層框架基礎:爲什麼不是JPA?-orm框架.png
可以看到,Hibernate使用佔比高達54%,而Mybatis的佔比僅有6%。這似乎和我們的直觀感覺有比較大的差異,因爲我們周圍使用Mybatis是多數,而Hibernate有比較高的學習成本,使用的並不多。至於基於Hibernate的spring data JPA,雖然有spring官方爲其背書,近些年發展迅猛,但仍然不是主流。所以爲什麼會有這種反直覺的結果呢?網上也有基於google搜索指數的分析。
java數據庫持久層框架基礎:爲什麼不是JPA?-D-Chat_20200524202848.png
從這張圖可以直觀發現,使用mybatis的主力主要集中在中國大陸、韓國和日本,而除此之外的絕大多數地區,都是Hibernate的天下。
    Mybatis在中國、韓國和日本流行的確切原因無法得知,知乎上很多人對這個現象有自己的猜測,當然也順帶隱晦的diss了Hibernat e/JPA。這種中間有多少“屁股決定腦袋”的原因,我無法知曉,但我猜測如果在國外的論壇有類似的帖子,Mybatis也逃不掉被diss的宿命。實際上,一種技術方案的產生必然是爲了解決某種場景下的問題,所以都有一定存在的價值。如果脫離場景去討論技術的優劣是無聊的爭執,根本沒了解一種技術的設計方案,就去判斷一個技術的好壞多少顯得有些愚蠢。
    由於Mybatis國內廣泛的使用,且Mybatis的使用門檻也不高,這裏過多討論Mybatis的設計並沒有太大的意義。而JPA標準是Hibernate、spring data jpa等ORM框架的基礎,對其有深入的理解就能明白全自動化的ORM框架的設計理念,或許能夠對大家以後的技術框架選型有幫助。

JPA主要思想

實體

    JPA的主要思想使用java普通類對象(這裏被稱爲實體,Entity)映射數據庫中表中的一條記錄,實體類的屬性定義對應表相應的字段結構,框架能夠根據這種映射實現實體對象持久化到數據庫表中。已經持久化到數據庫表中或者已經和數據庫表的記錄建立關聯的實體被稱爲託管(Manage)對象(Herbernate中被稱爲持久狀態,Persistent),處於託管狀態的實體對象的所有改動都會影響數據庫中對應的記錄。爲了清晰的區分實體是否生效(這裏是指是否能夠影響數據庫記錄),加上託管狀態,JPA標準一共定義了四種狀態:

  1. 瞬時(New):剛new出來的實體對象,無Id,還未和數據庫表記錄建立關聯
  2. 託管(Manage):已經和數據庫記錄建立關聯,所有改動都會影響數據庫記錄,但還未提交事務生效。
  3. 遊離(Datached):有Id值,但沒有和數據表記錄建立關聯,這裏主要是和事務相關。
  4. 刪除(Removed):有Id值,但是尚未和數據表記錄建立關聯。這裏主要也是和事務相關。
    java數據庫持久層框架基礎:爲什麼不是JPA?-entity-state.png

實體管理

    JPA定義了實體,同時也確定了實體類的狀態,剩下的是需要對實體進行管理,這就是JPA EntityManager的工作。EntityManager除了管理實體之外,還用來管理事務操作。綜合EntityManager的實體操作和事務操作,能夠對實體的狀態變化有更好的理解。
    一般的JPA數據庫CRUD操作包括三步:

  1. 使用EntityManager開啓事務
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
  1. 獲取實體並進行持久化操作
//這裏示例是創建的Movie Entity
Movie movie = new Movie();
movie.setLength(120);
movie.setName("gone with wind");
//持久化persist操作
entityManager.persist(movie);
  1. 提交事務
transaction.commit();

這裏new一個實體,比如上例中的new Movie(),這時這個實體的狀態就是瞬時狀態。當進行persist操作之後,此時實體狀態屬於託管狀態。當事務提交之後,此時實體已經被更新到數據庫表中,實體狀態變爲遊離狀態。從另一個方面,也就是說任何實體到數據庫的持久化操作,都需要提交事務,這也是爲什麼在使用JPA時,數據庫寫入或更新的方法需要@Transactional的原因。
    其他類似的實體狀態變化,可以參考上面的實體狀態變化圖。
    EntityManager底層屏蔽了數據庫的CRUD sql操作,這樣在使用JPA時,應用層和持久層是隔離的,即使更換了不同類型的數據庫,只需要JPA底層兼容sql即可。除了persist、merge等EntityManager操作數據庫的方法,spring data jpa還實現了使用Repository接口的方式。Repository接口的方法底層還是使用EntityManager,特色在於用方法名來識別各種查詢參數,聽起來就非常笨拙。不過爲了靈活性,EntityManager支持JPQL,JPQL是有着類似sql的語法,但是使用實體類代替數據庫表的結構來操作數據庫。EntityManager甚至支持原生sql操作,但是並不建議這樣使用。
    EntityMananger內部封裝了數據庫的連接操作,每個EntityManager都對應一個特定數據庫的連接池。這意味着如果需要實現分庫分表的數據庫訪問,需要配置不同的EntityManager。
    不同的EntityManager的管理由EntityManagerFactory實現,entityManagerFactory.createEntityManager()可以配置和創建一個EntityManager。
java數據庫持久層框架基礎:爲什麼不是JPA?-jpa structure.png

JPA設計思想的問題

    JPA 實體映射的思想很難合理地解決關係型數據庫聯表查詢的問題。如果只是單個表的操作,只需要對應一個實體類,JPA的操作數據庫可以說是非常簡單。但是多個表的聯合查詢,意味着各個實體類之間需要實現屬性的對應映射關係,這不是簡單的繼承或者組合能夠實現的。這個難點在JPA設計範疇裏仍然沒有比較好的解決方案,不過querydsl聯合JPA標準可以很簡便的解決這個問題。

//querydsl解決聯表問題的模板用法
 jpaQueryFactory.select().join().where();
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章