因?yàn)轫?xiàng)目需要選擇數(shù)據(jù)持久化框架胜卤,看了一下主要幾個(gè)流行的和不流行的框架尚揣,對(duì)于復(fù)雜業(yè)務(wù)系統(tǒng),最終的結(jié)論是,JOOQ是總體上最好的兽叮,可惜不是完全免費(fèi),最終選擇JDBC Template靡努。
【文末放送面試題扭仁,純福利】
Hibernate和Mybatis是使用最多的兩個(gè)主流框架,而JOOQ展蒂、Ebean等小眾框架則知道的人不多又活,但也有很多獨(dú)特的優(yōu)點(diǎn);而JPA則是一組Java持久層Api的規(guī)范锰悼,Spring Data JPA是JPA Repository的實(shí)現(xiàn)皇钞,本來(lái)和Hibernate、Mybatis松捉、JOOQ之類的框架不在同一個(gè)層次上夹界,但引入Spring Data JPA之類框架之后,我們會(huì)直接使用JPA的API查詢更新數(shù)據(jù)庫(kù)隘世,就像我們使用Mybatis一樣可柿,所以這里也把JPA和其他框架放在一起進(jìn)行比較。
同樣丙者,JDBC和其他框架也在同一層次复斥,位于所有持久框架的底層,但我們有時(shí)候也會(huì)直接在項(xiàng)目中使用JDBC械媒,而Spring JDBC Template部分消除了使用JDBC的繁瑣細(xì)節(jié)目锭,降低了使用成本,使得我們更加愿意在項(xiàng)目中直接使用JDBC纷捞。
一痢虹、SQL封裝和性能
在使用Hibernate的時(shí)候,我們查詢的是POJO實(shí)體類主儡,而不再是數(shù)據(jù)庫(kù)的表奖唯,例如hql語(yǔ)句 select count(*) from User,里面的User是一個(gè)Java類糜值,而不是數(shù)據(jù)庫(kù)表User丰捷。
這符合ORM最初的理想,ORM認(rèn)為Java程序員使用OO的思維方式寂汇,和關(guān)系數(shù)據(jù)庫(kù)的思維方式差距巨大病往,為了填補(bǔ)對(duì)象和關(guān)系思維方式的鴻溝,必須做一個(gè)對(duì)象到關(guān)系的映射骄瓣,然后在Java的對(duì)象世界中停巷,程序員可以使用純的對(duì)象的思維方式,查詢POJO對(duì)象,查詢條件是對(duì)象屬性叠穆,不再需要有任何表少漆、字段等關(guān)系的概念,這樣java程序員就更容易做持久層的操作硼被。
JPA可以視為Hibernate的兒子示损,也繼承了這個(gè)思路,把SQL徹底封裝起來(lái)嚷硫,讓Java程序員看不到關(guān)系的概念检访,用純的面向?qū)ο笏枷耄匦聞?chuàng)造一個(gè)新的查詢語(yǔ)言代替sql仔掸,比如hql脆贵,還有JPQL等。支持JPA的框架起暮,例如Ebean都屬于這種類型的框架卖氨。
但封裝SQL,使用另一種純的面向?qū)ο蟛樵冋Z(yǔ)言代替sql负懦,真的能夠讓程序員更容易實(shí)現(xiàn)持久層操作嗎筒捺?MyBatis的流行證明了事實(shí)并非如此,至少在大多數(shù)情況下纸厉,使用hql并不比使用sql簡(jiǎn)單系吭。
首先,從很多角度上看颗品,hql/JPQL等語(yǔ)言更加復(fù)雜和難以理解肯尺;其次就是性能上明顯降低,速度更慢躯枢,內(nèi)存占用巨大则吟,而且還不好優(yōu)化。最為惱火的是闺金,當(dāng)關(guān)系的概念被替換為對(duì)象的概念之后逾滥,查詢語(yǔ)言的靈活性變得很差峰档,表達(dá)能力也比sql弱很多败匹。寫查詢語(yǔ)句的時(shí)候受到各種各樣的限制,一個(gè)典型的例子就是多表關(guān)聯(lián)查詢讥巡。
不管是hibernate還是jpa掀亩,表之間的連接查詢,被映射為實(shí)體類之間的關(guān)聯(lián)關(guān)系欢顷,這樣槽棍,如果兩個(gè)實(shí)體類之間沒有(實(shí)現(xiàn))關(guān)聯(lián)關(guān)系,你就不能把兩個(gè)實(shí)體(或者表)join起來(lái)查詢。這是很惱火的事情炼七,因?yàn)槲覀兒芏鄷r(shí)候并不需要顯式定義兩個(gè)實(shí)體類之間的關(guān)聯(lián)關(guān)系就可以實(shí)現(xiàn)業(yè)務(wù)邏輯缆巧,如果使用hql,只是為了join我們就必須在兩個(gè)實(shí)體類之間添加代碼豌拙,而且還不能逆向工程陕悬,如果表里面沒有定義外鍵約束的話,逆向工程會(huì)把我們添加的關(guān)聯(lián)代碼抹掉按傅。
MyBatis則是另外一種類型的持久化框架捉超,它沒有封裝SQL也沒有創(chuàng)建一種新的面相對(duì)象的查詢語(yǔ)言,而是直接使用SQL作為查詢語(yǔ)言唯绍,只是把結(jié)果填入POJO對(duì)象而已拼岳。使用sql并不比hql和JPQL困難,查詢速度快况芒,可以靈活使用任意復(fù)雜的查詢只要數(shù)據(jù)庫(kù)支持惜纸。從SQL封裝角度上看,MyBatis比Hibernate和JPA成功绝骚,SQL本不該被封裝和隱藏堪簿,讓Java程序員使用SQL既不麻煩也更容易學(xué)習(xí)和上手,這應(yīng)該是MyBatis流行起來(lái)的重要原因皮壁。
輕量級(jí)持久層框架JOOQ也和MyBatis一樣椭更,直接使用SQL作為查詢語(yǔ)言,比起MyBatis蛾魄,JOOQ雖然知名度要低得多虑瀑,但JOOQ不但和MyBatis一樣可以利用SQL的靈活性和高效率,通過(guò)逆向工程滴须,JOOQ還可以用Java代碼來(lái)編寫SQL語(yǔ)句舌狗,利用IDE的代碼自動(dòng)補(bǔ)全功能,自動(dòng)提示表名和字段名扔水,減少程序員記憶負(fù)擔(dān)痛侍,還可以在元數(shù)據(jù)發(fā)生變化時(shí)發(fā)生編譯錯(cuò)誤,提示程序員修改相應(yīng)的SQL語(yǔ)句魔市。
Ebean作為一種基于JPA的框架主届,它也使用JPQL語(yǔ)言進(jìn)行查詢,多數(shù)情況下會(huì)讓人很惱火待德。但據(jù)說(shuō)Ebean不排斥SQL君丁,可以直接用SQL查詢,也可以用類似JOOQ的DSL方式在代碼中構(gòu)造SQL語(yǔ)句(還是JPQL語(yǔ)句将宪?)绘闷,但沒用過(guò)Ebean橡庞,所以具體細(xì)節(jié)不清楚。
JDBC Template就不用說(shuō)了印蔗,它根本沒做ORM扒最,當(dāng)然是純SQL查詢。利用Spring框架华嘹,可以把JDBC Template和JPA結(jié)合起來(lái)使用扼倘,在JPA不好查詢的地方,或者效率低不好優(yōu)化的地方使用JDBC除呵,緩解了Hibernate/JPA封裝SQL造成的麻煩再菊,但我仍沒看到任何封裝SQL的必要性,除了給程序員帶來(lái)一大堆麻煩和學(xué)習(xí)負(fù)擔(dān)之外颜曾,沒有太明顯的好處纠拔。
二、DSL和變化適應(yīng)性
為了實(shí)現(xiàn)復(fù)雜的業(yè)務(wù)邏輯泛豪,不論是用SQL還是hql或者JPQL稠诲,我們都不得不寫很多簡(jiǎn)單的或者復(fù)雜的查詢語(yǔ)句,ORM無(wú)法減少這部分工作诡曙,最多是用另一種面向?qū)ο箫L(fēng)格的語(yǔ)言去表達(dá)查詢需求臀叙,如前所述,用面向?qū)ο箫L(fēng)格的語(yǔ)言不見得比SQL更容易价卤。
通常業(yè)務(wù)系統(tǒng)中會(huì)有很多表劝萤,每個(gè)表都有很多字段,即便是編寫最簡(jiǎn)單的查詢語(yǔ)句也不是一件容易的事情慎璧,需要記住數(shù)據(jù)庫(kù)中有哪些表床嫌,有哪些字段,記住有哪些函數(shù)等胸私。寫查詢語(yǔ)句很多時(shí)候成為一件頭疼的事情厌处。
QueryDSL、JOOQ岁疼、Ebean甚至MyBatis和JPA都設(shè)計(jì)一些特性阔涉,幫助開發(fā)人員編寫查詢語(yǔ)句,有人稱之為“DSL風(fēng)格數(shù)據(jù)庫(kù)編程”捷绒。最早實(shí)現(xiàn)這類功能的可能是QueryDSL瑰排,把數(shù)據(jù)庫(kù)的表結(jié)構(gòu)逆向工程為java的類,然后可以讓java程序員能夠用java的語(yǔ)法構(gòu)造出一個(gè)復(fù)雜的查詢語(yǔ)句疙驾,利用IDE的代碼自動(dòng)補(bǔ)全功能凶伙,可以自動(dòng)提示表名、字段名它碎、查詢語(yǔ)句的關(guān)鍵字等,很成功的簡(jiǎn)化了查詢語(yǔ)句的編寫,免除了程序員記憶各種名字扳肛、函數(shù)和關(guān)鍵字的負(fù)擔(dān)傻挂。
QueryDSL有很多版本,但用得多的是QueryDSL JPA挖息,可以幫助開發(fā)人員編寫JPQL語(yǔ)句金拒,如前所述,JPQL語(yǔ)句有很多局限不如SQL靈活高效套腹。后來(lái)的JOOQ和Ebean绪抛,基本上繼承了QueryDSL的思路,Ebean基本上還是JPA風(fēng)格的ORM框架电禀,雖然也支持SQL幢码,但不清楚其DSL特性是否支持SQL語(yǔ)句編寫,在官網(wǎng)上看到的例子都是用于構(gòu)造JPQL語(yǔ)句尖飞。
這里面最成功的應(yīng)該是JOOQ症副,和QueryDSL不同,JOOQ的DSL編程是幫助開發(fā)人員編寫SQL語(yǔ)句政基,拋棄累贅的ORM概念贞铣,JOOQ這個(gè)功能非常輕小,非常容易學(xué)習(xí)和使用沮明,同時(shí)性能也非常好辕坝,不像QueryDSL和Ebean,需要了解復(fù)雜的JPA概念和各種奇異的限制荐健,JOOQ編寫的就是普通的SQL語(yǔ)句圣勒,只是把查詢結(jié)果填充到實(shí)體類中(嚴(yán)格說(shuō)JOOQ沒有實(shí)體類,只是自動(dòng)生成的Record對(duì)象)摧扇,JOOQ甚至不一定要把結(jié)果轉(zhuǎn)換為實(shí)體類圣贸,可以讓開發(fā)人員按照字段取得結(jié)果的值,相對(duì)于JDBC扛稽,JOOQ會(huì)把結(jié)果值轉(zhuǎn)換為合適的Java類型吁峻,用起來(lái)比JDBC更簡(jiǎn)單。
傳統(tǒng)主流的框架對(duì)DSL風(fēng)格支持得很少在张,Hibernate里面基本上沒有看到有這方面的特性用含。MyBatis提供了"SQL語(yǔ)句構(gòu)建器"來(lái)幫助開發(fā)人員構(gòu)造SQL語(yǔ)句,但和QueryDSL/JOOQ/Ebean差很多帮匾,不能提示表名和字段名啄骇,語(yǔ)法也顯得累贅不像SQL。
JPA給人的印象是復(fù)雜難懂瘟斜,它的MetaModel Api繼承了特點(diǎn)缸夹,MetaModel API+Criteria API痪寻,再配合Hibernate JPA 2 Metamodel Generator,讓人有點(diǎn)QueryDSL JPA的感覺虽惭,只是繞了一個(gè)大大的彎橡类,疊加了好幾層技術(shù),最后勉強(qiáng)實(shí)現(xiàn)了QueryDSL JPA的簡(jiǎn)單易懂的功能芽唇。很多人不推薦JPA+QueryDSL的用法顾画,而是推薦JPA MetaModel API+Criteria API+Hibernate JPA 2 Metamodel Generator的用法,讓人很難理解匆笤,也許是因?yàn)檫@個(gè)方案是純的標(biāo)準(zhǔn)的JPA方案研侣。
數(shù)據(jù)庫(kù)DSL編程的另一個(gè)主要賣點(diǎn)是變化適應(yīng)性強(qiáng),數(shù)據(jù)庫(kù)表結(jié)構(gòu)在開發(fā)過(guò)程中通常會(huì)頻繁發(fā)生變化炮捧,傳統(tǒng)的非DSL編程庶诡,字段名只是一個(gè)字符串,如果字段名或者類型改變之后寓盗,查詢語(yǔ)句沒有相應(yīng)修改灌砖,編譯不會(huì)出錯(cuò),也容易被開發(fā)人員忽略傀蚌,是bug的一個(gè)主要來(lái)源基显。DSL編程里面,字段被逆向工程為一個(gè)java類的屬性善炫,數(shù)據(jù)庫(kù)結(jié)構(gòu)改變之后撩幽,作為java代碼一部分的查詢語(yǔ)句會(huì)發(fā)生編譯錯(cuò)誤,提示開發(fā)人員進(jìn)行修改箩艺,可以減少大量bug窜醉,減輕測(cè)試的負(fù)擔(dān),提高軟件的可靠性和質(zhì)量艺谆。
三榨惰、跨數(shù)據(jù)庫(kù)移植
Hibernate和JPA使用hql和JPQL這類數(shù)據(jù)庫(kù)無(wú)關(guān)的中間語(yǔ)言描述查詢,可以在不同數(shù)據(jù)庫(kù)中無(wú)縫移植静汤,移植到一個(gè)SQL有巨大差別的數(shù)據(jù)庫(kù)通常不需要修改代碼或者只需要修改很少的代碼琅催。Ebean如果不使用原生SQL,而是使用JPA的方式開發(fā)虫给,也能在不同數(shù)據(jù)庫(kù)中平滑的移植藤抡。
MyBatis和JOOQ直接使用SQL,跨數(shù)據(jù)庫(kù)移植時(shí)都難免要修改SQL語(yǔ)句抹估。這方面MyBatis比較差缠黍,只有一個(gè)動(dòng)態(tài)SQL提供的特性,對(duì)于不同的數(shù)據(jù)庫(kù)編寫不同的sql語(yǔ)句药蜻。
JOOQ雖然無(wú)法像Hibernate和JPA那樣無(wú)縫移植瓷式,但比MyBatis好很多替饿。JOOQ的DSL很大一部分是通用的,例如分頁(yè)查詢中蒿往,Mysql的limit/offset關(guān)鍵字是很方便的描述方式盛垦,但Oracle和SQLServer的SQL不支持湿弦,如果我們用JOOQ的DSL的limit和offset方法構(gòu)造SQL語(yǔ)句瓤漏,不修改移植到不支持limit/offset的Oracle和SQLServer上,我們會(huì)發(fā)現(xiàn)這些語(yǔ)句還能正常使用颊埃,因?yàn)镴OOQ會(huì)把limit/offset轉(zhuǎn)換成等價(jià)的目標(biāo)數(shù)據(jù)庫(kù)的SQL語(yǔ)句蔬充。JOOQ根據(jù)目標(biāo)數(shù)據(jù)庫(kù)轉(zhuǎn)換SQL語(yǔ)句的特性,使得在不同數(shù)據(jù)庫(kù)之間移植的時(shí)候班利,只需要修改很少的代碼饥漫,明顯優(yōu)于MyBatis。
JDBC Template應(yīng)該最差罗标,只能盡量使用標(biāo)準(zhǔn)sql語(yǔ)句來(lái)減少移植工作量庸队。
四、安全性
一般來(lái)說(shuō)闯割,拼接查詢語(yǔ)句都會(huì)有安全隱患彻消,容易被sql注入攻擊。不論是jdbc宙拉,還是hql/JPQL宾尚,只要使用拼接的查詢語(yǔ)句都是不安全的。對(duì)于JDBC來(lái)說(shuō)谢澈,使用參數(shù)化的sql語(yǔ)句代替拼接煌贴,可以解決問(wèn)題。而JPA則應(yīng)該使用Criteria API解決這個(gè)問(wèn)題锥忿。
對(duì)于JOOQ之類的DSL風(fēng)格框架牛郑,最終會(huì)被render為參數(shù)化的sql,天生免疫sql注入攻擊敬鬓。Ebean也支持DSL方式編程淹朋,也同樣免疫sql注入攻擊。
這是因?yàn)镈SL風(fēng)格編程參數(shù)化查詢比拼接字符串查詢更簡(jiǎn)單列林,沒人會(huì)拼接字符串瑞你。而jdbc/hql/JPQL拼接字符串有時(shí)候比參數(shù)化查詢更簡(jiǎn)單,特別是jdbc希痴,很多人會(huì)偷懶使用不安全的方式者甲。
五、JOOQ的失敗之處
可能大部分人會(huì)不同意砌创,雖然Hibernate虏缸、JPA仍然大行其道鲫懒,是最主流的持久化框架,但其實(shí)這種封裝SQL的純正ORM已經(jīng)過(guò)時(shí)刽辙,效益低于使用它們的代價(jià)窥岩,應(yīng)該淘汰了。MyBatis雖然有很多優(yōu)點(diǎn)宰缤,但它的優(yōu)點(diǎn)JOOQ基本上都有颂翼,而且多數(shù)還更好。MyBatis最大的缺點(diǎn)是難以避免寫xml文件慨灭,xml文件編寫困難朦乏,容易出錯(cuò),還不容易查找錯(cuò)誤氧骤。相對(duì)于JOOQ呻疹,MyBatis在多數(shù)情況下沒有任何優(yōu)勢(shì)。
Ebean同時(shí)具有很多不同框架的優(yōu)點(diǎn)筹陵,但它是基于JPA的刽锤,難免有JPA的各種限制,這是致命的缺點(diǎn)朦佩。
JOOQ這個(gè)極端輕量級(jí)的框架技術(shù)上是最完美的并思,突然有一天幾個(gè)Web系統(tǒng)同時(shí)崩了,最后發(fā)現(xiàn)是JOOQ試用期過(guò)期了吕粗,這是JOOQ的失敗之處纺荧,它不是完全免費(fèi)的,只是對(duì)MySql之類的開源數(shù)據(jù)庫(kù)免費(fèi)颅筋。
最終宙暇,我決定選擇JDBC Template。
2020 最新 Java 面試題出爐R楸谩(帶全部答案)
題庫(kù)非常全面占贫,包括 Java 集合、JVM先口、多線程型奥、Spring全家桶、Redis碉京、Dubbo厢汹、Netty、Elasticsearch谐宙、大數(shù)據(jù)烫葬、阿里巴巴等大廠面試題等。
看下方領(lǐng)取4钭邸垢箕!
添加小助手VX:xuanwo008
部分面試題預(yù)覽內(nèi)容如下
部分內(nèi)容截圖如下