已阅读5页,还剩13页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
在 Hibernate 中直接操作 JDBC 接口 Hibernate 在处理多表关联及分组排序等复杂数据库查询操作时,其固有的 O-R 映射机制会产生大量冗余 SQL 操作,系统性能比传统的 JDBC 低很多。本文分析了 Hibernate 产生此类问题的原因,提出了一个在 Hibernate 框架内直接操作 JDBC 的接口的解决方案,在实际在 Hibernate 框架中提供直接操作 JDBC 接口的原因Hibernate 框架在处理复杂查询方面的问题Hibernate 是一个开放源代码的对象关系映射框架,它对 JDBC 进行了非常轻量级的对象封装,使得 Java 程序员可以随心所欲的使用面向对象编程思维来操纵数据库。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. hibernate 实体配置 xmlA.hbm.xml:B.hbm.xml:C.hbm.xml对应的 Hibernate 领域实体类代码示例如下:清单 2. hibernate 实体类示例A.java:publicclassAimplementsjava.io.Serializable,Comparableprivatelongid;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(curAmountotherAmount)return1;elsereturn0;B.java:publicclassBimplementsjava.io.Serializable,Comparableprivatelongid;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,Comparableprivatelongid;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;假设现在要统计 A 表中从属的 B 表和 C 表记录之和最高的 top10 的 A 表记录,在 Hibernate 框架下,由于取 A 表对应的数据库记录时,已关联取出了对应的 B、C 表子记录存放于 A 实体类的 children_a,children_c 的属性中,因此 top10 的功能可以通过比较每个 A 表实体类中 children_a、children_c 的 Set 的 size 大小并进行排序得到,其代码示例如下:清单 3. 排序代码示例privateArrayListsortAByAmount(ArrayListall)for(inti=0;iall.size();i+)for(intj=0;jall.size()-i-1;+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 是面向对象的 O-R 映射机制,每一条 A 表记录的查询实际都关联有两条 B、C 表查询的 SQL 产生,我们可以看到 Hibernate 的 SQL 日志中:清单 4. Hibernate sql 日志示例Hibernate:selecta0_.IDasID2_fromAa0_wherea0_.ID=1Hibernate:selectb0_.IDasID2_,b0_.A_IDasA_ID2_fromBb0_whereb0_.ID=?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”决定的。这种情况下,当 A 和 B、C 表中数据量越来越大时,A 表取实体的操作开销将随着 sql 查询的增多而增大,并且在紧接着的排序过程中,即使采用业界最快的快速排序算法,排序时间依然是随原始排序实体数量的线性关系(O(n lg n)),效率会线性下降,最终无法满足客户的前台查询的效率要求。此类情况下如直接采用 JDBC,则只需一条如下的 SQL 语句,即可完成该功能:清单 5. 直接 JDBC 操作 sqlselecttab1.ID,tab1.sumCol+tab2.sumColfrom(selecta.ID,count(b.ID)sumColfromAaleftjoinBbona.ID=b.IDGROUPBYa.ID)tab1,(selecta.ID,count(c.ID)sumColfromAaleftjoinCcona.ID=c.IDGROUPBYa.ID)tab2wheretab1.ID=tab2.IDorderbytab1.sumCol+tab2.sumColdesc在以上 JDBC 方式下,即使 A、B、C 表的数据量持续增长,仍然只有 1 条 SQL 的开销,不会出现 SQL 递增的情况,因此耗时是在可控制的区间内的。并且读者可以注意到上述 SQL 将 3 表关联拆分成了 2 个子查询,这样避免了 3 表做笛卡尔积的数量和,进一步提高了查询效率。由此可见,直接操作 JDBC,除 SQL 的开销可控外,还可以利用数据库层各种机制,如上述查询语句中的 left join、子查询、索引,灵活的调整 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 是单线程的,代表了一次会话的过程。实际上是把一个 JDBC Connection 打包了,每一个 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 该方法以完成自有领域对象的填充细节。实际实现的类代码如下所示:清单 6. 解决方案实现代码AbstractHibernateDao.java:abstractpublicclassAbstractHibernateDaoextendsHibernateDaoSupportprotectedLoglogger=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:abstractpublicclassBaseHibernateDaoextendsAbstractHibernateDaopublicConnectiongetConnection()trySessioncurSeesion=null;Connectioncon=null;curSeesion=super.getHibernateTemplate().getSessionFactory().getCurrentSession();con=curSeesion.connection();returncon;catch(Exceptiones)System.out.println(es.getMessage();returnnull;publicArrayListfetchObjects(ResultSetrs)ArrayListret=newArrayList();/example:/while(rs.next()/Objectobject=newObject();/rs.getString(1);/rs.getString(2);/ret.add(object);/returnret;publicArrayListgetObjectsBySql(StringpureSql)Connectioncon=curSeesion.connection();ps=con.prepareStatement(sqlbuf.toString();rs=ps.executeQuery();tryreturnthis.fetchObjects(rs);catch(Exceptiones)System.out.println(es.getMessage();returnnull;finallytryps.close();rs.close();con.close();catch(SQLExceptione)/TODOAuto-generatedcatchblocke.printStackTrace();使用该解决方案时,只需要将代码包解压至项目源代码目录,在想要直接操作 JDBC 接口的 DAO 模块继承 BaseHibernateDao 类,然后重写 fetchObjects 方法填入从自身数据库表字段填充到领域对象的操作 , 即可轻松调用 getObjectsBySql 传入原生 SQL 语句,返回具体的领域对象实体集合,当然使用者也可以通过 getConnection 获得 JDBC 的 Connection 实例来进行自己需要的特定的 JDBC 底层操作。仍然以上文中的 A、B、C 表为例,采用该解决方案完成 top10 取数的代码示例如下:清单 7. 使用解决方案示例publicclasstestDAOextendsBaseHibernateDaoprivateStringsqlQuery=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;overridepublicArrayListfetchObjects(ResultSetrs)ArrayListret=newArrayList();intcount=1;while(rs.next()Aa=newA();a.setId(rs.getLong(1);System.out.println(top+(
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2026住院患者静脉血栓栓塞症的预防护理2023教学课件
- 生化检验员安全知识宣贯考核试卷含答案
- 碳酸锂蒸发工安全意识强化知识考核试卷含答案
- 铁合金焙烧操作工创新方法水平考核试卷含答案
- 26年基因检测公共卫生适配要点
- 26年非霍奇金淋巴瘤判读要点
- 26年HER2低表达检测用药指导
- 2026 减脂期肉馅课件
- 2025年美发师中级实操考核试卷:美发师客户满意度调查与分析试题
- 游离皮片移植围手术期护理常规
- 纪委书记岗位面试题集
- 汉字复合笔画课件
- 工装保洁合同协议
- 电池PACK生产项目商业计划书
- 2026上药控股有限公司校园招聘(公共基础知识)综合能力测试题附答案解析
- 导演专业:影视创作新势力
- ISO9001-2026质量管理体系管理评审计划管理评审报告及各部门管理评审资料
- 零件读本(出书版)-1
- 2025年压力性损伤考试题(+答案解析)
- 2025年温州市泰顺县教育局县城学校选调教师考试笔试试卷【附答案】
- DB37-T 5087-2021 建筑与市政工程绿色施工评价标准
评论
0/150
提交评论