新聞中心
Hibernate是一個開放源代碼的對象關(guān)系映射框架,它對JDBC進(jìn)行了非常輕量級的對象封裝,使得Java程序員可以隨心所欲的使用面向?qū)ο缶幊趟季S來操縱數(shù)據(jù)庫。

成都創(chuàng)新互聯(lián)公司專注為客戶提供全方位的互聯(lián)網(wǎng)綜合服務(wù),包含不限于成都網(wǎng)站設(shè)計(jì)、成都做網(wǎng)站、坡頭網(wǎng)絡(luò)推廣、成都微信小程序、坡頭網(wǎng)絡(luò)營銷、坡頭企業(yè)策劃、坡頭品牌公關(guān)、搜索引擎seo、人物專訪、企業(yè)宣傳片、企業(yè)代運(yùn)營等,從售前售中售后,我們都將竭誠為您服務(wù),您的肯定,是我們最大的嘉獎;成都創(chuàng)新互聯(lián)公司為所有大學(xué)生創(chuàng)業(yè)者提供坡頭建站搭建服務(wù),24小時服務(wù)熱線:028-86922220,官方網(wǎng)址:www.cdcxhl.com
Hibernate的優(yōu)勢在于屏蔽了數(shù)據(jù)庫細(xì)節(jié),對于新增修改刪除的數(shù)據(jù)層操作,不再需要跟具體的SQL語句打交道,簡單的對對象實(shí)例進(jìn)行增刪改操作即可。但是,對于多表關(guān)聯(lián)、分組統(tǒng)計(jì)、排序等復(fù)雜的查詢功能時,由于Hibernate自身的O-R映射機(jī)制,父子表之間關(guān)聯(lián)取數(shù)據(jù)會產(chǎn)生大量冗余的查詢操作,性能低下。此類情況下,直接使用JDBC的SQL語句反而更加靈活和高效。
Hibernate框架處理復(fù)雜查詢問題實(shí)例分析
考慮如下數(shù)據(jù)庫實(shí)體示例,表A為主表,表B和表C為子表,A與B、A與C表均為1對多關(guān)系,在B表和C表中以A_ID外鍵字段關(guān)聯(lián)A表父記錄。
圖 1.數(shù)據(jù)庫實(shí)體示例圖
在Hibernate框架中,通常采用以下配置方式完成A表與B,C表父子實(shí)體之間的級聯(lián)查詢操作,Hibernate實(shí)體配置xml如下:
- 清單1.hibernate實(shí)體配置xml
- A.hbm.xml:
- B.hbm.xml:
- C.hbm.xml
對應(yīng)的Hibernate領(lǐng)域?qū)嶓w類代碼示例如下:
- 清單2.hibernate實(shí)體類示例
- A.java:
- publicclassAimplementsjava.io.Serializable,Comparable{
- privatelongid;
- privateSetchildren_b=newHashSet();
- privateSetchildren_c=newHashSet
(); - publicA(longid){
- this.id=id;
- }
- publiclonggetId(){
- returnid;
- }
- publicvoidsetId(longid){
- this.id=id;
- }
- publicSetgetChildern_b(){
- returnchildren_b;
- }
- publicvoidsetChildren_b(Setchildren_b){
- this.children_b=children_b;
- }
- publicSetgetChildern_c(){
- returnchildren_c;
- }
- publicvoidsetChildren_c(Setchildren_c){
- this.children_c=children_c;
- }
- publicintcompareTo(Objectother){
- AotherSubject=(A)other;
- longcurAmount=this.getChildren_b().size()+this.getChildren_c().size();
- longotherAmount=otherSubject.getChildren_b().size()
- +otherSubject.getChildren_c().size();
- if(curAmount
- {
- return-1;
- }
- elseif(curAmount>otherAmount)
- {
- return1;
- }
- else
- {
- return0;
- }
- }
- }
- B.java:
- publicclassBimplementsjava.io.Serializable,Comparable{
- privatelongid;
- privatelonga_id;
- publiclonggetId(){
- returnid;
- }
- publicvoidsetId(longid){
- this.id=id;
- }
- publiclonggetA_id(){
- returna_id;
- }
- publicvoidsetA_id(longa_id){
- this.a_id=a_id;
- }
- publicB(longid){
- this.id=id;
- }
- }
- C.java:
- publicclassCimplementsjava.io.Serializable,Comparable{
- privatelongid;
- privatelonga_id;
- publiclonggetId(){
- returnid;
- }
- publicvoidsetId(longid){
- this.id=id;
- }
- publiclonggetA_id(){
- returna_id;
- }
- publicvoidsetA_id(longa_id){
- this.a_id=a_id;
- }
- publicC(longid){
- this.id=id;
- }
- }
假設(shè)現(xiàn)在要統(tǒng)計(jì)A表中從屬的B表和C表記錄之和最高的top10的A表記錄,在Hibernate框架下,由于取A表對應(yīng)的數(shù)據(jù)庫記錄時,已關(guān)聯(lián)取出了對應(yīng)的B、C表子記錄存放于A實(shí)體類的children_a,children_c的屬性中,因此top10的功能可以通過比較每個A表實(shí)體類中children_a、children_c的Set的size大小并進(jìn)行排序得到,其代碼示例如下:
- 清單3.排序代碼示例
- privateArrayListsortAByAmount(ArrayListall)
- {
- for(inti=0;i
- {
- for(intj=0;j
- {
- if(all.get(j).compareTo(all.get(j+1))<=0)
- {
- Atemp=all.get(j);
- all.set(j,all.get(j+1));
- all.set(j+1,temp);
- }
- }
- }
- returnall;
- }
表面看來很方便,但是由于Hibernate是面向?qū)ο蟮腛-R映射機(jī)制,每一條A表記錄的查詢實(shí)際都關(guān)聯(lián)有兩條B、C表查詢的SQL產(chǎn)生,我們可以看到Hibernate的SQL日志中:
- 清單4.Hibernatesql日志示例
- Hibernate:selecta0_.IDasID2_fromAa0_wherea0_.ID='1'
- Hibernate:selectb0_.IDasID2_,b0_.A_IDasA_ID2_fromBb0_whereb0_.ID=?
- Hibernate:selectc0_.IDasID2_,c0_.A_IDasA_ID2_fromCc0_wherec0_.ID=?
由上述Sql日志可以看出,每一條A表記錄的取出,都伴隨以A表ID為查詢條件關(guān)聯(lián)B,C表中A_ID外鍵字段的2條取子記錄的sql,這是由A.hbm.xml配置中“l(fā)azy=false”決定的。#p#
這種情況下,當(dāng)A和B、C表中數(shù)據(jù)量越來越大時,A表取實(shí)體的操作開銷將隨著sql查詢的增多而增大,并且在緊接著的排序過程中,即使采用業(yè)界最快的快速排序算法,排序時間依然是隨原始排序?qū)嶓w數(shù)量的線性關(guān)系(O(nlgn)),效率會線性下降,最終無法滿足客戶的前臺查詢的效率要求。此類情況下如直接采用JDBC,則只需一條如下的SQL語句,即可完成該功能:
- 清單5.直接JDBC操作sql
- select
- tab1.ID,
- tab1.sumCol+tab2.sumCol
- from
- (
- selecta.ID,
- count(b.ID)sumCol
- fromAaleftjoinBbona.ID=b.ID
- GROUPBY
- a.ID
- )tab1,
- (
- selecta.ID,
- count(c.ID)sumCol
- fromAaleftjoinCcona.ID=c.ID
- GROUPBY
- a.ID
- )tab2
- wheretab1.ID=tab2.ID
- orderbytab1.sumCol+tab2.sumColdesc
在以上JDBC方式下,即使A、B、C表的數(shù)據(jù)量持續(xù)增長,仍然只有1條SQL的開銷,不會出現(xiàn)SQL遞增的情況,因此耗時是在可控制的區(qū)間內(nèi)的。并且讀者可以注意到上述SQL將3表關(guān)聯(lián)拆分成了2個子查詢,這樣避免了3表做笛卡爾積的數(shù)量和,進(jìn)一步提高了查詢效率。由此可見,直接操作JDBC,除SQL的開銷可控外,還可以利用數(shù)據(jù)庫層各種機(jī)制,如上述查詢語句中的leftjoin、子查詢、索引…,靈活的調(diào)整SQL語句,以達(dá)到最佳的查詢性能。
由上可實(shí)例可看出,在多表關(guān)聯(lián)、排序等復(fù)雜的查詢情況下,Hibernate框架由于其自身對象封裝的特殊性,不能像JDBC直接操作SQL那樣很好的解決查詢中高效性和靈活性方面的需求,且由于其屏蔽了數(shù)據(jù)庫的底層,開發(fā)人員看到的只是Hibernate提供的數(shù)據(jù)層API,無法與靈活的使用SQL語句等數(shù)據(jù)庫底層細(xì)節(jié)。因此,有必要在Hibernate框架中提供直接操作JDBC的接口。
在Hibernate框架中提供操作JDBC的接口的解決方案
Hibernate的session機(jī)制
我們知道Hibernate框架本身也是建立在JDBC之上的數(shù)據(jù)持久層實(shí)現(xiàn),因此,要在框架本身提供操作JDBC的接口,需要切入其對JDBC封裝的細(xì)節(jié)。通過研究和查閱Hibernate的框架源代碼及參考文檔,我們發(fā)現(xiàn),Hibernate的Session會話是進(jìn)行持久化的基礎(chǔ),所有的持久化操作都是在Session的基礎(chǔ)上進(jìn)行的,在實(shí)現(xiàn)上它是和JDBC中的connection數(shù)據(jù)庫連接綁定的,也就是說,Hibernate的會話域基于一個實(shí)際的connection類實(shí)例,二者之間的關(guān)系如下圖所示:
圖 2.Hibernate Session機(jī)制示意圖
由上可以看到,Hibernate中的session是單線程的,代表了一次會話的過程。實(shí)際上是把一個JDBCConnection打包了,每一個Session實(shí)例和一個數(shù)據(jù)庫事務(wù)綁定。其生命周期是與與之關(guān)聯(lián)的connection實(shí)例的生命周期一致的。
具體解決方案
由上面的Hibernate的Session機(jī)制我們意識到,只要能獲取到Hibernate當(dāng)前會話中的Connection,則獲得了JDBC的底層數(shù)據(jù)庫連接實(shí)例,剩下就都是JDBC的范疇了。再查閱Hibernate的API,發(fā)現(xiàn)HibernateTemplate類中SessionFactory成員的getCurrentSession()方法即可獲得Hibernate環(huán)境下的當(dāng)前活動的Session會話,而Hibernate中Session實(shí)例的connection()方法即可獲得該會話中綁定的Connection數(shù)據(jù)庫連接實(shí)例。
問題迎刃而解了,既然可以操作Connection實(shí)例,那與之關(guān)聯(lián)的Statement、ResultSet等基本JDBC類均在我們控制范圍中了,我們采用接口模式設(shè)計(jì)一個輕量級解決方案,使其在保持原Hibernate的增刪改操作方式前提下靈活提供操作JDBC的接口。設(shè)計(jì)類圖如下圖所示:
圖 3.解決方案設(shè)計(jì)類示意圖
設(shè)計(jì)中,AbstractHibernateDao類作為DAO操作的基本類,保留原有Hibenrate框架下的新增,修改,刪除等API。BaseHibernateDao類繼承AbstractHibernateDao類,在此類中增加了直接操作JDBC的接口。設(shè)計(jì)getConnection方法獲取JDBC的數(shù)據(jù)庫連接實(shí)例,設(shè)計(jì)getObjectsBySql方法作為對外的主要接口,該方法調(diào)用fetchObjects方法,這是具體的數(shù)據(jù)庫記錄到領(lǐng)域?qū)ο蟮霓D(zhuǎn)換操作,需要使用者override該方法以完成自有領(lǐng)域?qū)ο蟮奶畛浼?xì)節(jié)。實(shí)際實(shí)現(xiàn)的類代碼如下所示:
- 清單6.解決方案實(shí)現(xiàn)代碼
- AbstractHibernateDao.java:
- abstractpublicclassAbstractHibernateDaoextendsHibernateDaoSupport{
- protectedLoglogger=LogFactory.getLog(getClass());
- protectedClassentityClass;
- protectedClassgetEntityClass(){
- returnentityClass;
- }
- publicListgetAll(){
- returngetHibernateTemplate().loadAll(getEntityClass());
- }
- publicvoidsave(Objecto){
- getHibernateTemplate().saveOrUpdate(o);
- }
- publicvoidremoveById(Serializableid){
- remove(get(id));
- }
- publicvoidremove(Objecto){
- getHibernateTemplate().delete(o);
- }
- }
- BaseHibernateDao.java:
- abstractpublicclassBaseHibernateDaoextendsAbstractHibernateDao{
- publicConnectiongetConnection()
- {
- try
- {
- SessioncurSeesion=null;
- Connectioncon=null;
- curSeesion=super.getHibernateTemplate().getSessionFactory()
- .getCurrentSession();
- con=curSeesion.connection();
- returncon;
- }
- catch(Exceptiones)
- {
- System.out.println(es.getMessage());
- returnnull;
- }
- }
- publicArrayList
- {
- ArrayList
- //example:
- //while(rs.next())
- //{
- //Objectobject=newObject();
- //rs.getString(1);
- //rs.getString(2);
- //ret.add(object);
- //}
- returnret;
- }
- publicArrayList
getObjectsBySql(StringpureSql) - {
- Connectioncon=curSeesion.connection();
- ps=con.prepareStatement(sqlbuf.toString());
- rs=ps.executeQuery();
- try
- {
- returnthis.fetchObjects(rs);
- }
- catch(Exceptiones)
- {
- System.out.println(es.getMessage());
- returnnull;
- }
- finally
- {
- try
- {
- ps.close();
- rs.close();
- con.close();
- }
- catch(SQLExceptione){
- //TODOAuto-generatedcatchblock
- e.printStackTrace();
- }
- }
- }
- }
使用該解決方案時,只需要將代碼包解壓至項(xiàng)目源代碼目錄,在想要直接操作JDBC接口的DAO模塊繼承BaseHibernateDao類,然后重寫fetchObjects方法填入從自身數(shù)據(jù)庫表字段填充到領(lǐng)域?qū)ο蟮牟僮?即可輕松調(diào)用getObjectsBySql傳入原生SQL語句,返回具體的領(lǐng)域?qū)ο髮?shí)體集合,當(dāng)然使用者也可以通過getConnection獲得JDBC的Connection實(shí)例來進(jìn)行自己需要的特定的JDBC底層操作。仍然以上文中的A、B、C表為例,采用該解決方案完成top10取數(shù)的代碼示例如下:
- 清單7.使用解決方案示例
- publicclasstestDAOextendsBaseHibernateDao{
- privateStringsqlQuery="selecttab1.ID,tab1.sumCol+tab2.sumCol"+
- "from(selecta.ID,count(b.ID)sumCol"+
- "fromAaleftjoinBbona.ID=b.ID"+
- "GROUPBYa.ID)tab1,"+
- "(selecta.ID,count(c.ID)sumCol"+
- "fromAaleftjoinCcona.ID=c.ID"+
- "GROUPBYa.ID)tab2"+
- "wheretab1.ID=tab2.ID"+
- "orderbytab1.sumCol+tab2.sumColdesc";
- @override
- publicArrayListfetchObjects(ResultSetrs)
- {
- ArrayListret=newArrayList();
- intcount=1;
- while(rs.next())
- {
- Aa=newA();
- a.setId(rs.getLong(1));
- System.out.println("top"+(count++)+"amount:"+rs.getLong(2));
- ret.add(object);
- }
- returnret;
- }
- }
解決方案驗(yàn)證
在實(shí)際mySql數(shù)據(jù)庫環(huán)境中,以A表數(shù)據(jù)量1000條,B表數(shù)據(jù)量3W多條,C表數(shù)據(jù)量2000條情況下進(jìn)行上文中提到的top 10的操作,采用Hibernate的耗時和用JDBC接口解決方案的效率比較如下:
表 1.Hibernate框架方式與JDBC接口方式效率比較
表 2.Hibernate框架方式與JDBC接口方式效率比較
由以上結(jié)果可以看出:在數(shù)據(jù)量遞增的情況下,采用Hibernate方式下效率與庫表數(shù)據(jù)呈線性增長,且排序的操作的效率也是一樣,而直接采用JDBC接口解決方案下效率遠(yuǎn)遠(yuǎn)高于Hibernate方式,且在數(shù)據(jù)量增長的情況下耗時的增長速度處于合理的區(qū)間內(nèi)。
文章名稱:Hibernate框架中直接操作JDBC接口實(shí)例
URL網(wǎng)址:http://fisionsoft.com.cn/article/dphiccj.html


咨詢
建站咨詢
