在 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 实体配置 xml A.hbm.xml: B.hbm.xml: C.hbm.xml 对应的 Hibernate 领域实体类代码示例如下: 清单 2. hibernate 实体类示例 A.java: public class A implements java.io.Serializable,Comparable { private long id; private Set children_b = new HashSet(); private Set children_c = new HashSet(); public A(long id) { this.id = id; } public long getId() { return id; } public void setId(long id) { this.id = id; } public Set getChildern_b() { return children_b; } public void setChildren_b (Set children_b) { this.children_b = children_b; } public Set getChildern_c() { return children_c; } public void setChildren_c (Set children_c) { this.children_c = children_c; } public int compareTo(Object other) { A otherSubject = (A)other; long curAmount=this.getChildren_b().size()+this.getChildren_c().size(); long otherAmount =otherSubject.getChildren_b().size() + otherSubject.getChildren_c().size(); if(curAmountotherAmount) { return 1; } else { return 0; } } } B.java: public class B implements java.io.Serializable,Comparable { private long id; private long a_id; public long getId() { return id; } public void setId(long id) { this.id = id; } public long getA_id() { return a_id; } public void setA_id(long a_id) { this.a_id = a_id; } public B(long id) { this.id=id; } } C.java: public class C implements java.io.Serializable,Comparable { private long id; private long a_id; public long getId() { return id; } public void setId(long id) { this.id = id; } public long getA_id() { return a_id; } public void setA_id(long a_id) { this.a_id = a_id; } public C(long id) { 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. 排序代码示例 private ArrayList sortAByAmount(ArrayList all) { for(int i=0;i fetchObjects(ResultSet rs) { ArrayList ret = new ArrayList(); //example: //while(rs.next()) //{ //Object object = new Object(); //rs.getString(1); //rs.getString(2); //ret.add(object); //} return ret; } public ArrayList getObjectsBySql(String pureSql) { Connection con = curSeesion.connection(); ps = con.prepareStatement(sqlbuf.toString()); rs = ps.executeQuery(); try { return this.fetchObjects(rs); } catch(Exception es) { System.out.println(es.getMessage()); return null; } finally { try { ps.close(); rs.close(); con.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } 使用该解决方案时,只需要将代码包解压至项目源代码目录,在想要直接操作 JDBC 接口的 DAO 模块继承 BaseHibernateDao 类,然后重写 fetchObjects 方法填入从自身数据库表字段填充到领域对象的操作 , 即可轻松调用 getObjectsBySql 传入原生 SQL 语句,返回具体的领域对象实体集合,当然使用者也可以通过 getConnection 获得 JDBC 的 Connection 实例来进行自己需要的特定的 JDBC 底层操作。 仍然以上文中的 A、B、C 表为例,采用该解决方案完成 top10 取数的代码示例如下: 清单 7. 使用解决方案示例 public class testDAO extends BaseHibernateDao{ private String sqlQuery = " select tab1.ID,tab1.sumCol+tab2.sumCol"+ " from(select a.ID, count(b.ID) sumCol"+ " from A a left join B b on a.ID=b.ID"+ " GROUP BY a.ID)tab1, "+ " (select a.ID,count(c.ID) sumCol"+ " from A a left join C c on a.ID=c.ID"+ " GROUP BY a.ID)tab2"+ " where tab1.ID=tab2.ID"+ " order by tab1.sumCol+tab2.sumCol desc"; @override public ArrayList fetchObjects(ResultSet rs) { ArrayList ret = new ArrayList(); int count=1; while(rs.next()) { A a = new A(); a.setId(rs.getLong(1)); System.out.println("top"+(count++)+" amount:"+rs.getLong(2)); ret.add(object); } return ret; } } 解决方案验证 在实际 mySql 数据库环境中,以 A 表数据量 1000 条,B 表数据量 3W 多条,C 表数据量 2000 条情况下进行上文中提到的 top10 的操作,采用 Hibernate 的耗时和用 JDBC 接口解决方案的效率比较如下: 表 1. Hibernate 框架方式与 JDBC 接口方式效率比较 1: Hibernate 框架方式 采用 JDBC 接口解决方案 查询耗时 1475ms 1096ms 排序耗时 1035ms 0ms 总计: 2110ms 1096ms A 表数据量 2000 条,B 表数据量 6W,C 表数据量 4000 条情况下,采用 Hibernate 的耗时和用 JDBC 接口解决方案的效率比较: 表 2. Hibernate 框架方式与 JDBC 接口方式效率比较 2: Hibernate 框架方式 采用 JDBC 接口解决方案 查询耗时 2836ms 1657ms 排序耗时 1568ms 0ms 总计: 4404ms 1657ms 由以上结果可以看出:在数据量递增的情况下,采用 Hibernate 方式下效率与库表数据呈线性增长,且排序的操作的效率也是一样,而直接采用 JDBC 接口解决方案下效率远远高于 Hibernate 方式,且在数据量增长的情况下耗时的增长速度处于合理的区间内。 回页首 总结 本文分析了 Hibernate 框架在处理复杂查询功能上的效率问题,提出并实现了一个在 Hibernate 框架内提供直接 JDBC 操作接口的解决方案,并实际验证了该解决方案的有效性,文中的源代码可以直接运用于选择 Hibenrate 框架作为数据持久层实现的 J2EE 项目,使之具备操作底层 JDBC 的功能。

posted on 2012-05-20 23:21  ycty  阅读(248)  评论(0编辑  收藏  举报