一、現狀描述
目前java 持久層ORM框架應用最廣泛的就是JPA和Mybatis。JPA只是一個ORM框架的規范, 對該規范的實現比較完整就是Spring Data JPA(底層基於Hibernate實現),是基於Spring的數據持久層框架,也就是說它只能用在Spring環境內。Mybatis也是一個優秀的數據持久層框架,能比較好的支持ORM實體關系映射、動態SQL等。
筆者在學習這兩個框架的過程中,看過不少的帖子,每當有帖子比較這兩個框架的優缺點,就引來一場論戰。從筆者的角度,為什么國內的開發人員或者開發團隊較少使用JPA?為了避免有人抨擊我,我特意去做了一下國內某度指數搜索,這個數據騙不了人。
圖中藍色線條為Mybatis搜索量,綠色為JPA搜索量。如果你換一個國外的搜索指數,你會得到一個完全不同的結果。那么這是為什么呢?我們還要從JPA的特點說起:
- JPA對於單表的或者簡單的SQL查詢非常友好,甚至可以說非常智能。他為你准備好了大量的拿來即用的持久層操作方法。甚至只要寫findByName這樣一個接口方法,他就能智能的幫你執行根據名稱查找實體類對應的表數據,完全不用寫SQL。
- 但是,JPA對於多表關聯查詢以及動態SQL、自定義SQL等非常不友好。對於JPA來說,一種實現實現方式是QueryDSL,實現的代碼是下面這樣的。我想問:你希望用這樣的代碼代替SQL么?
JPAQueryFactory queryFactory = new JPAQueryFactory(em);
JPAQuery<Tuple> jpaQuery = queryFactory.select(QTCity.tCity,QTHotel.tHotel)
.from(QTCity.tCity)
.leftJoin(QTHotel.tHotel)
.on(QTHotel.tHotel.city.longValue().eq(QTCity.tCity.id.longValue()));
//添加查詢條件
jpaQuery.where(predicate);
//拿到結果
return jpaQuery.fetch();
另一種方法是使用NativeQuery,我仍然想問:你希望在java代碼里面用拼字符串的方式寫SQL么?
@Entity
@NamedNativeQueries(value={
@NamedNativeQuery(
name = "studentInfoById",
query = " SELECT * FROM student_info "
+ " WHERE stu_id = ? ",
resultClass = Student.class
)
})
@Table(name="student_info")
以上的這部分實現還沒有考慮到動態SQL的問題,如果考慮到動態SQL,寫法會更復雜。所謂的動態SQL就是:根據傳入參數條件的不同,構造不同的SQL,很多的比較這兩個框架的文章都忽略了動態SQL的問題,這方面Mybatis支持的更好。Mybatis寫的動態SQL說到底還是SQL,而不是java代碼或者java代碼拼字符串。程序員特別排斥幾件事:
- 將復雜關聯關系的SQL寫在java代碼里面,拼串書寫不方便
- SQL是最能表達實體關系查詢的語言,程序員不希望使用異化SQL語言。
- 程序員不希望學習不通用的東西,顯然SQL大家都會
- JPA雖然將大部分操作封裝起來了,也挺好用的,但是SQL調優怎么做?
可以使用Spring-Data-JPA-extra解決JPA的拼串書寫和SQL異化的問題。但是根據筆者的使用情況,Spring-Data-JPA-extra是一個個人開發者項目,用於生產還很不成熟,對於多數據源處理、復雜類型處理等還有很多的問題。
二、劣幣驅逐良幣?
然而,另外有一派觀點,你看人家國外的程序員怎么都用JPA?你不去學習新東西,還不讓別人用?JPA使用很方便啊,唯一缺點就是復雜關聯SQL支持差一點,但是只要你學一下也還可以支持啊,你們這是劣幣驅逐良幣。如果經過很好的實體關系模型的設計,JPA顯然是最優解,程序員寫的SQL還真不如JPA根據實體關系生成的SQL。筆者要說,這種觀點也是有道理的。但是,筆者要說並不是國內程序員不願意學習,而是另有原因。
- 首先,筆者長年從事遠程工作,與國外程序員接觸較多。他們習慣使用JPA的一個原因,真的是因為他們國家的應用規模太小了,比起國內的一個應用動則上百萬的用戶相比,他們在數據庫設計與調優的需求上顯然更從容。
- 國外的應用設計往往更簡潔,而國內的應用需求往往功能性更強。如果不信,你可以去看看工作流,什么會簽、流程回退什么的都是我們發明的,他們沒有。你讓他們用JPA寫一個我們的工作流應用試一試,累吐血他們也做不到。
- 異化SQL或者代碼里面寫SQL,一定程度上增加了學習成本和使用成本。所以用的人少,用的人少你就得遷就團隊中的大部分人。
說完以上幾點,Mybatis為什么在國內會有如此多的使用者及使用廠商就不難理解了。Mybatis還可以使用如:Mybatis-plus或者代碼自動生成來彌補易用性上的不足。JPA的身材、家室、性格樣樣都是滿分,就是臉長得磕磣點難以處理社交關系。Mybatis雖說在各方面都不優秀,身材還可以、樣貌也還說得過去、性格也還好。關鍵是你說什么都聽你的,還有願意幫他化妝的朋友。要你說你選哪一個?
那么,有的人會說,你這是抬杠?國外就沒有受眾數量多、功能性強的互聯網應用了么?恐怕比國內還多吧,這個也是事實。但是從比例上講還是國內更多,比例決定開發人員選擇技術的方向。這也導致了一個慣性思維,他們平時就用JPA學習訓練,所以寫大型服務應用的時候也用JPA。那么,他們寫JPA會寫復雜SQL么?答案是很少會用到,甚至有的國外公司就明令禁止寫關聯查詢SQL。那怎么辦?不用關聯SQL怎么開發業務需求?不會啊。
三、服務拆分或微服務
國內現在也有越來越多的公司,進行微服務的落地,然而真正落地比較好的企業少之又少。這和多表關聯查詢有什么關系?我們先來實現這樣一個需求:查詢屬於A角色相關的所有的業務B數據。
- 如果我們開發的是傳統的單體應用,我們可能是把角色表A和業務表B進行關聯查詢,然后得到查詢結果
- 如果我們做的是微服務,我們可能是拆分為權限服務A、業務服務B。先去訪問A服務接口獲取角色標志信息,然后再根據角色標志信息去業務服務B接口獲取業務數據。
那么有的人會說,訪問兩個接口一定比訪問一個接口更慢吧!這個真的不是,如果我們做微服務,一定是我們的應用規模及數據量到達了一定程度。也一定會考慮分表分庫、負載均衡、服務拆分細化等問題,當分布式的開發方式被應用越多,多表關聯查詢使用的機會也就越少。拆分后的服務由於功能單一、負載分流等原因,訪問速度往往比大數據量數據集中存儲、多服務集中部署的應用會快很多。
問題回來了,不用關聯SQL怎么開發程序?總的來說就是通過合理的服務拆分、應用的界面數據的組織關系的合理的設計,團隊擁有比較好的微服務落地經驗,是可以實現不使用關聯查詢SQL開發應用的。大家也知道,NOSQL越來越流行,絕大部分的NOSQL數據庫都沒有所謂的關聯關系。
四、框架對比選型
對比項 | Spring Data JPA | Mybatis |
---|---|---|
單表操作方式 | 只需繼承,代碼量極少,非常方便。而且支持方法名用關鍵字生成SQL | 可以使用代碼生成工具或Mybatis-Plus等工具,也很方便,但相對JPA要弱一些。 |
多表關聯查詢 | 不太友好,動態SQL使用不夠方便,而且SQL和代碼耦合到一起 | 友好,可以有非常直觀的動態SQL |
自定義SQL | SQL寫在注解里面,寫動態SQL有些費勁 | SQL可以寫在XML里面,是書寫動態SQL語法利器。也支持注解SQL。 |
學習成本 | 略高 | 較低 ,基本會寫SQL就會用 |
總結一下筆者的觀點:
- 如果你是自己開發“小而美”的應用,建議你使用JPA
- 如果你是開發大而全的企業級應用,當然要遵從團隊的技術選型。這個技術選型在國內通常是Mybatis。
- 如果你們公司的管理非常規范,微服務落地經驗也非常成熟,可以考慮在團隊項目中使用JPA。少用或不用關聯查詢。
期待您的關注
- 博主最近新寫了一本書:《手摸手教您學習SpringBoot系列-16章97節》
- 本文轉載注明出處(必須帶連接,不能只轉文字):字母哥博客。