Hibernate框架中直接操作JDBC接口实例

Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,使得Java程序员可以随心所欲的使用面向对象编程思维来操纵数据库。

成都创新互联公司专注为客户提供全方位的互联网综合服务,包含不限于成都网站设计、成都做网站、坡头网络推广、成都微信小程序、坡头网络营销、坡头企业策划、坡头品牌公关、搜索引擎seo、人物专访、企业宣传片、企业代运营等,从售前售中售后,我们都将竭诚为您服务,您的肯定,是我们最大的嘉奖;成都创新互联公司为所有大学生创业者提供坡头建站搭建服务,24小时服务热线:028-86922220,官方网址:www.cdcxhl.com

Hibernate的优势在于屏蔽了数据库细节,对于新增修改删除的数据层操作,不再需要跟具体的SQL语句打交道,简单的对对象实例进行增删改操作即可。但是,对于多表关联、分组统计、排序等复杂的查询功能时,由于Hibernate自身的O-R映射机制,父子表之间关联取数据会产生大量冗余的查询操作,性能低下。此类情况下,直接使用JDBC的SQL语句反而更加灵活和高效。

Hibernate框架处理复杂查询问题实例分析

考虑如下数据库实体示例,表A为主表,表B和表C为子表,A与B、A与C表均为1对多关系,在B表和C表中以A_ID外键字段关联A表父记录。

 
图 1.数据库实体示例图

在Hibernate框架中,通常采用以下配置方式完成A表与B,C表父子实体之间的级联查询操作,Hibernate实体配置xml如下:

 
 
 
 
  1. 清单1.hibernate实体配置xml  
  2. A.hbm.xml:  
  3.  
  4.  
  5.  
  6.  
  7.  
  8.  
  9.  
  10.  
  11.  
  12.  
  13.  
  14.  
  15.  
  16.  
  17.  
  18.  
  19.  
  20.  
  21. B.hbm.xml:  
  22.  
  23.  
  24.  
  25.  
  26.  
  27.  
  28.  
  29.  
  30.  
  31.  
  32.  
  33.  
  34.  
  35.  
  36. C.hbm.xml  
  37.  
  38.  
  39.  
  40.  
  41.  
  42.  
  43.  
  44.  
  45.  
  46.  
  47.  
  48.  
  49.  

对应的Hibernate领域实体类代码示例如下:

 
 
 
 
  1. 清单2.hibernate实体类示例  
  2. A.java:  
  3. publicclassAimplementsjava.io.Serializable,Comparable{  
  4. privatelongid;  
  5. privateSetchildren_b=newHashSet();  
  6. privateSetchildren_c=newHashSet();  
  7.  
  8. publicA(longid){  
  9. this.id=id;  
  10. }  
  11.  
  12. publiclonggetId(){  
  13. returnid;  
  14. }  
  15.  
  16. publicvoidsetId(longid){  
  17. this.id=id;  
  18. }  
  19.  
  20. publicSetgetChildern_b(){  
  21. returnchildren_b;  
  22. }  
  23.  
  24. publicvoidsetChildren_b(Setchildren_b){  
  25. this.children_b=children_b;  
  26. }  
  27.  
  28. publicSetgetChildern_c(){  
  29. returnchildren_c;  
  30. }  
  31.  
  32. publicvoidsetChildren_c(Setchildren_c){  
  33. this.children_c=children_c;  
  34. }  
  35.  
  36. publicintcompareTo(Objectother){  
  37. AotherSubject=(A)other;  
  38. longcurAmount=this.getChildren_b().size()+this.getChildren_c().size();  
  39. longotherAmount=otherSubject.getChildren_b().size()  
  40.  +otherSubject.getChildren_c().size();  
  41. if(curAmount
  42. {  
  43. return-1;  
  44. }  
  45. elseif(curAmount>otherAmount)  
  46. {  
  47. return1;  
  48. }  
  49. else  
  50. {  
  51. return0;  
  52. }  
  53. }  
  54. }  
  55.  
  56. B.java:  
  57. publicclassBimplementsjava.io.Serializable,Comparable{  
  58. privatelongid;  
  59. privatelonga_id;  
  60.  
  61. publiclonggetId(){  
  62. returnid;  
  63. }  
  64.  
  65. publicvoidsetId(longid){  
  66. this.id=id;  
  67. }  
  68.  
  69. publiclonggetA_id(){  
  70. returna_id;  
  71. }  
  72.  
  73. publicvoidsetA_id(longa_id){  
  74. this.a_id=a_id;  
  75. }  
  76.  
  77. publicB(longid){  
  78. this.id=id;  
  79. }  
  80. }  
  81.  
  82. C.java:  
  83. publicclassCimplementsjava.io.Serializable,Comparable{  
  84. privatelongid;  
  85. privatelonga_id;  
  86.  
  87. publiclonggetId(){  
  88. returnid;  
  89. }  
  90.  
  91. publicvoidsetId(longid){  
  92. this.id=id;  
  93. }  
  94.  
  95. publiclonggetA_id(){  
  96. returna_id;  
  97. }  
  98.  
  99. publicvoidsetA_id(longa_id){  
  100. this.a_id=a_id;  
  101. }  
  102.  
  103. publicC(longid){  
  104. this.id=id;  
  105. }  

假设现在要统计A表中从属的B表和C表记录之和最高的top10的A表记录,在Hibernate框架下,由于取A表对应的数据库记录时,已关联取出了对应的B、C表子记录存放于A实体类的children_a,children_c的属性中,因此top10的功能可以通过比较每个A表实体类中children_a、children_c的Set的size大小并进行排序得到,其代码示例如下:

 
 
 
 
  1. 清单3.排序代码示例  
  2. privateArrayListsortAByAmount(ArrayListall)  
  3. {  
  4. for(inti=0;i
  5. {  
  6. for(intj=0;j
  7. {  
  8. if(all.get(j).compareTo(all.get(j+1))<=0)  
  9. {  
  10. Atemp=all.get(j);  
  11. all.set(j,all.get(j+1));  
  12. all.set(j+1,temp);  
  13. }  
  14. }  
  15. }  
  16. returnall;  

表面看来很方便,但是由于Hibernate是面向对象的O-R映射机制,每一条A表记录的查询实际都关联有两条B、C表查询的SQL产生,我们可以看到Hibernate的SQL日志中:

 
 
 
 
  1. 清单4.Hibernatesql日志示例  
  2. Hibernate:selecta0_.IDasID2_fromAa0_wherea0_.ID='1' 
  3. Hibernate:selectb0_.IDasID2_,b0_.A_IDasA_ID2_fromBb0_whereb0_.ID=?  
  4. Hibernate:selectc0_.IDasID2_,c0_.A_IDasA_ID2_fromCc0_wherec0_.ID=? 

由上述Sql日志可以看出,每一条A表记录的取出,都伴随以A表ID为查询条件关联B,C表中A_ID外键字段的2条取子记录的sql,这是由A.hbm.xml配置中“lazy=false”决定的。#p#

这种情况下,当A和B、C表中数据量越来越大时,A表取实体的操作开销将随着sql查询的增多而增大,并且在紧接着的排序过程中,即使采用业界最快的快速排序算法,排序时间依然是随原始排序实体数量的线性关系(O(nlgn)),效率会线性下降,最终无法满足客户的前台查询的效率要求。此类情况下如直接采用JDBC,则只需一条如下的SQL语句,即可完成该功能:

 
 
 
 
  1. 清单5.直接JDBC操作sql  
  2. select  
  3. tab1.ID,  
  4. tab1.sumCol+tab2.sumCol  
  5. from  
  6. (  
  7. selecta.ID,  
  8. count(b.ID)sumCol  
  9. fromAaleftjoinBbona.ID=b.ID  
  10. GROUPBY  
  11. a.ID  
  12. )tab1,  
  13. (  
  14. selecta.ID,  
  15. count(c.ID)sumCol  
  16. fromAaleftjoinCcona.ID=c.ID  
  17. GROUPBY  
  18. a.ID  
  19. )tab2  
  20. wheretab1.ID=tab2.ID  
  21. orderbytab1.sumCol+tab2.sumColdesc 

在以上JDBC方式下,即使A、B、C表的数据量持续增长,仍然只有1条SQL的开销,不会出现SQL递增的情况,因此耗时是在可控制的区间内的。并且读者可以注意到上述SQL将3表关联拆分成了2个子查询,这样避免了3表做笛卡尔积的数量和,进一步提高了查询效率。由此可见,直接操作JDBC,除SQL的开销可控外,还可以利用数据库层各种机制,如上述查询语句中的leftjoin、子查询、索引…,灵活的调整SQL语句,以达到最佳的查询性能。

由上可实例可看出,在多表关联、排序等复杂的查询情况下,Hibernate框架由于其自身对象封装的特殊性,不能像JDBC直接操作SQL那样很好的解决查询中高效性和灵活性方面的需求,且由于其屏蔽了数据库的底层,开发人员看到的只是Hibernate提供的数据层API,无法与灵活的使用SQL语句等数据库底层细节。因此,有必要在Hibernate框架中提供直接操作JDBC的接口。

在Hibernate框架中提供操作JDBC的接口的解决方案

Hibernate的session机制

我们知道Hibernate框架本身也是建立在JDBC之上的数据持久层实现,因此,要在框架本身提供操作JDBC的接口,需要切入其对JDBC封装的细节。通过研究和查阅Hibernate的框架源代码及参考文档,我们发现,Hibernate的Session会话是进行持久化的基础,所有的持久化操作都是在Session的基础上进行的,在实现上它是和JDBC中的connection数据库连接绑定的,也就是说,Hibernate的会话域基于一个实际的connection类实例,二者之间的关系如下图所示:

 
图 2.Hibernate Session机制示意图

由上可以看到,Hibernate中的session是单线程的,代表了一次会话的过程。实际上是把一个JDBCConnection打包了,每一个Session实例和一个数据库事务绑定。其生命周期是与与之关联的connection实例的生命周期一致的。

具体解决方案

由上面的Hibernate的Session机制我们意识到,只要能获取到Hibernate当前会话中的Connection,则获得了JDBC的底层数据库连接实例,剩下就都是JDBC的范畴了。再查阅Hibernate的API,发现HibernateTemplate类中SessionFactory成员的getCurrentSession()方法即可获得Hibernate环境下的当前活动的Session会话,而Hibernate中Session实例的connection()方法即可获得该会话中绑定的Connection数据库连接实例。

问题迎刃而解了,既然可以操作Connection实例,那与之关联的Statement、ResultSet等基本JDBC类均在我们控制范围中了,我们采用接口模式设计一个轻量级解决方案,使其在保持原Hibernate的增删改操作方式前提下灵活提供操作JDBC的接口。设计类图如下图所示:

 
图 3.解决方案设计类示意图

设计中,AbstractHibernateDao类作为DAO操作的基本类,保留原有Hibenrate框架下的新增,修改,删除等API。BaseHibernateDao类继承AbstractHibernateDao类,在此类中增加了直接操作JDBC的接口。设计getConnection方法获取JDBC的数据库连接实例,设计getObjectsBySql方法作为对外的主要接口,该方法调用fetchObjects方法,这是具体的数据库记录到领域对象的转换操作,需要使用者override该方法以完成自有领域对象的填充细节。实际实现的类代码如下所示:

 
 
 
 
  1. 清单6.解决方案实现代码  
  2. AbstractHibernateDao.java:  
  3. abstractpublicclassAbstractHibernateDaoextendsHibernateDaoSupport{  
  4.  
  5. protectedLoglogger=LogFactory.getLog(getClass());  
  6. protectedClassentityClass;  
  7.  
  8. protectedClassgetEntityClass(){  
  9. returnentityClass;  
  10. }  
  11.  
  12. publicListgetAll(){  
  13. returngetHibernateTemplate().loadAll(getEntityClass());  
  14. }  
  15.  
  16. publicvoidsave(Objecto){  
  17. getHibernateTemplate().saveOrUpdate(o);  
  18. }  
  19.  
  20. publicvoidremoveById(Serializableid){  
  21. remove(get(id));  
  22. }  
  23.  
  24. publicvoidremove(Objecto){  
  25. getHibernateTemplate().delete(o);  
  26. }  
  27.  
  28. }  
  29.  
  30. BaseHibernateDao.java:  
  31. abstractpublicclassBaseHibernateDaoextendsAbstractHibernateDao{  
  32. publicConnectiongetConnection()  
  33. {  
  34. try  
  35. {  
  36.  
  37. SessioncurSeesion=null;  
  38. Connectioncon=null;  
  39. curSeesion=super.getHibernateTemplate().getSessionFactory()  
  40.  .getCurrentSession();  
  41. con=curSeesion.connection();  
  42. returncon;  
  43. }  
  44. catch(Exceptiones)  
  45. {  
  46. System.out.println(es.getMessage());  
  47. returnnull;  
  48. }  
  49.  
  50. }  
  51.  
  52. publicArrayListfetchObjects(ResultSetrs)  
  53. {  
  54. ArrayListret=newArrayList();  
  55. //example:  
  56. //while(rs.next())  
  57. //{  
  58. //Objectobject=newObject();  
  59. //rs.getString(1);  
  60. //rs.getString(2);  
  61. //ret.add(object);      
  62. //}  
  63. returnret;  
  64. }  
  65.  
  66. publicArrayListgetObjectsBySql(StringpureSql)  
  67. {  
  68. Connectioncon=curSeesion.connection();  
  69. ps=con.prepareStatement(sqlbuf.toString());  
  70. rs=ps.executeQuery();  
  71. try  
  72. {  
  73. returnthis.fetchObjects(rs);   
  74.      
  75. }  
  76. catch(Exceptiones)  
  77. {  
  78. System.out.println(es.getMessage());  
  79. returnnull;  
  80. }  
  81. finally  
  82. {  
  83. try  
  84. {  
  85. ps.close();  
  86. rs.close();  
  87. con.close();  
  88. }  
  89. catch(SQLExceptione){  
  90. //TODOAuto-generatedcatchblock  
  91. e.printStackTrace();  
  92. }  
  93.      
  94. }  
  95. }  
  96. 使用该解决方案时,只需要将代码包解压至项目源代码目录,在想要直接操作JDBC接口的DAO模块继承BaseHibernateDao类,然后重写fetchObjects方法填入从自身数据库表字段填充到领域对象的操作,即可轻松调用getObjectsBySql传入原生SQL语句,返回具体的领域对象实体集合,当然使用者也可以通过getConnection获得JDBC的Connection实例来进行自己需要的特定的JDBC底层操作。仍然以上文中的A、B、C表为例,采用该解决方案完成top10取数的代码示例如下:

     
     
     
     
    1. 清单7.使用解决方案示例  
    2. publicclasstestDAOextendsBaseHibernateDao{  
    3.  
    4. privateStringsqlQuery="selecttab1.ID,tab1.sumCol+tab2.sumCol"+  
    5. "from(selecta.ID,count(b.ID)sumCol"+  
    6. "fromAaleftjoinBbona.ID=b.ID"+  
    7. "GROUPBYa.ID)tab1,"+  
    8. "(selecta.ID,count(c.ID)sumCol"+  
    9. "fromAaleftjoinCcona.ID=c.ID"+  
    10. "GROUPBYa.ID)tab2"+  
    11. "wheretab1.ID=tab2.ID"+  
    12. "orderbytab1.sumCol+tab2.sumColdesc";  
    13.  
    14. @override  
    15. publicArrayListfetchObjects(ResultSetrs)  
    16. {  
    17. ArrayListret=newArrayList();  
    18. intcount=1;  
    19. while(rs.next())  
    20. {  
    21. Aa=newA();  
    22. a.setId(rs.getLong(1));  
    23. System.out.println("top"+(count++)+"amount:"+rs.getLong(2));  
    24. ret.add(object);      
    25. }  
    26. returnret;  
    27. }  
    28.  

    解决方案验证

    在实际mySql数据库环境中,以A表数据量1000条,B表数据量3W多条,C表数据量2000条情况下进行上文中提到的top 10的操作,采用Hibernate的耗时和用JDBC接口解决方案的效率比较如下:

     
    表 1.Hibernate框架方式与JDBC接口方式效率比较

     
    表 2.Hibernate框架方式与JDBC接口方式效率比较

    由以上结果可以看出:在数据量递增的情况下,采用Hibernate方式下效率与库表数据呈线性增长,且排序的操作的效率也是一样,而直接采用JDBC接口解决方案下效率远远高于Hibernate方式,且在数据量增长的情况下耗时的增长速度处于合理的区间内。

    当前标题:Hibernate框架中直接操作JDBC接口实例
    网址分享:http://www.shufengxianlan.com/qtweb/news44/490894.html

    网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

    广告

    声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联