关于Hibernate获取JDBC连接 直接执行SQL
2012-03-23 16:38
429 查看
/article/4491511.html
今天要做一个显示数据库列表的功能,不想直接用JDBC,呵呵于是用hibernate,由于对hibernate 不熟悉,想执行一条语句"show databases" 都找不到办法...
在网上找了下,说是使用 session.connection(),但是在 hibernate3中,这个方法不推荐使用,但是将就先用吧
晚上回来看了下资料,原来jdbc是另外有组件来管理的, 使用方式如下:做下记录:
ConnectionProvider cp = ConnectionProviderFactory.newConnectionProvider();
Connection conn = null;
//这里是获取连接
try {
conn = cp.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
//关闭连接这样用,连接才能够被放回连接池
try {
cp.closeConnection(conn);
} catch (SQLException e) {
e.printStackTrace();
}
http://www.ibm.com/developerworks/cn/java/j-lo-hibernate-jdbc/index.html?ca=drs-
在 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: <hibernate-mapping> <class name="XXX.XXX.A" table="A" > <id name="id" type="long"> <column name="ID"/> <generator class="assigned"> </generator> </id> <set name="children_B" cascade="delete" inverse="true" lazy="false"> <key column="A_ID"></key> <one-to-many class="XXX.XXX.B"/> </set> <set name="children_C" cascade="delete" inverse="true" lazy="false"> <key column="A_ID"></key> <one-to-many class="XXX.XXX.C"/> </set> </class> </hibernate-mapping> B.hbm.xml: <hibernate-mapping> <class name="XXX.XXX.B" table="B" > <id name="id" type="long"> <column name="ID"/> <generator class="assigned"> </generator> </id> <property name="a_id" type="long"> <column name="A_ID"> </column> </property> </class> </hibernate-mapping> C.hbm.xml <hibernate-mapping> <class name="XXX.XXX.C" table="C" > <id name="id" type="long"> <column name="ID"/> <generator class="assigned"> </generator> </id> <property name="a_id" type="long"> <column name="A_ID"> </column> </property> </class> </hibernate-mapping> |
清单 2. hibernate 实体类示例
A.java: public class A implements java.io.Serializable,Comparable { private long id; private Set children_b = new HashSet<B>(); private Set children_c = new HashSet<C>(); 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(curAmount<otherAmount) { return -1; } else if(curAmount>otherAmount) { 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; } } |
清单 3. 排序代码示例
private ArrayList<A> sortAByAmount(ArrayList<A> all) { for(int i=0;i<all.size();i++) { for(int j=0;j<all.size()-i-1;++j) { if(all.get(j).compareTo(all.get(j+1))<=0) { A temp = all.get(j); all.set(j,all.get(j+1)); all.set(j+1,temp); } } } return all; } |
清单 4. Hibernate sql 日志示例
Hibernate: select a0_.ID as ID2_ from A a0_ where a0_.ID='1' Hibernate: select b0_.ID as ID2_,b0_.A_ID as A_ID2_ from B b0_ where b0_.ID=? Hibernate: select c0_.ID as ID2_,c0_.A_ID as A_ID2_ from C c0_ where c0_.ID=? |
这种情况下,当 A 和 B、C 表中数据量越来越大时,A 表取实体的操作开销将随着 sql 查询的增多而增大,并且在紧接着的排序过程中,即使采用业界最快的快速排序算法,排序时间依然是随原始排序实体数量的线性关系(O(n lg n)),效率会线性下降,最终无法满足客户的前台查询的效率要求。
此类情况下如直接采用 JDBC,则只需一条如下的 SQL 语句,即可完成该功能:
清单 5. 直接 JDBC 操作 sql
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 |
由上可实例可看出,在多表关联、排序等复杂的查询情况下,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: abstract public class AbstractHibernateDao extends HibernateDaoSupport { protected Log logger = LogFactory.getLog(getClass()); protected Class entityClass; protected Class getEntityClass() { return entityClass; } public List getAll() { return getHibernateTemplate().loadAll(getEntityClass()); } public void save(Object o) { getHibernateTemplate().saveOrUpdate(o); } public void removeById(Serializable id) { remove(get(id)); } public void remove(Object o) { getHibernateTemplate().delete(o); } } BaseHibernateDao.java: abstract public class BaseHibernateDao extends AbstractHibernateDao{ public Connection getConnection() { try { Session curSeesion =null; Connection con =null; curSeesion = super.getHibernateTemplate().getSessionFactory() .getCurrentSession(); con = curSeesion.connection(); return con; } catch(Exception es) { System.out.println(es.getMessage()); return null; } } public ArrayList<Object> fetchObjects(ResultSet rs) { ArrayList<Object> ret = new ArrayList<Object>(); //example: //while(rs.next()) //{ //Object object = new Object(); //rs.getString(1); //rs.getString(2); //ret.add(object); //} return ret; } public ArrayList<Object> 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(); } } } } |
仍然以上文中的 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<A> fetchObjects(ResultSet rs) { ArrayList<A> ret = new ArrayList<A>(); 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 |
表 2. Hibernate 框架方式与 JDBC 接口方式效率比较
2:
Hibernate 框架方式 | 采用 JDBC 接口解决方案 | |
---|---|---|
查询耗时 | 2836ms | 1657ms |
排序耗时 | 1568ms | 0ms |
总计: | 4404ms | 1657ms |
回页首
总结
本文分析了 Hibernate 框架在处理复杂查询功能上的效率问题,提出并实现了一个在 Hibernate 框架内提供直接 JDBC 操作接口的解决方案,并实际验证了该解决方案的有效性,文中的源代码可以直接运用于选择 Hibenrate 框架作为数据持久层实现的 J2EE 项目,使之具备操作底层 JDBC 的功能。
回页首
下载
描述 | 名字 | 大小 | 下载方法 |
---|---|---|---|
本文源代码 | code.zip | 3 KB | HTTP |
http://www.23book.net/SoftwareDev/Java/53683.htm
相关文章推荐
- 关于Hibernate获取JDBC连接 直接执行SQL - 客观,辩证,务实,创新 - JavaEye技术网站
- 关于Hibernate获取JDBC连接 直接执行SQL
- weblogic hibernate关于No Dialect mapping for JDBC type :-9 hibernate执行原生sql语句问题(www.50xiao.com)
- 关于No Dialect mapping for JDBC type :-9 hibernate执行原生sql语句问题
- 关于No Dialect mapping for JDBC type :-9 hibernate执行原生sql语句问题
- 关于No Dialect mapping for JDBC type :-9 hibernate执行原生sql语句问题
- 关于No Dialect mapping for JDBC type :-9 hibernate执行原生sql语句问题 .
- 关于No Dialect mapping for JDBC type :-9 hibernate执行原生sql语句问题
- 关于在java中连接MS SQL Server 2005 JDBC的问题及解决办法
- Hibernate中关于多表连接查询hql 和 sql 返回值集合中对象问题
- (转)Hibernate中关于多表连接查询hql 和 sql 返回值集合中对象问题
- hibernate JDBCExceptionReporter : SQL Error: 17008, SQLState: null 关闭的连接
- 关于java基础--SQL驱动及JDBC连接
- 【救助】关于JDBC连接数据库改造成Hibernate连接数据库
- 关于EF中直接执行sql语句的参数化问题
- Hibernate3.2与Sql连接时出现的--java.lang.AbstractMethodError: com.microsoft.jdbc.base.BaseDatabaseMetaData.supportsGetGeneratedKeys()Z
- 关于hibernate执行sql语句后,进程不关闭的问题
- JDBC之数据库的连接与简单的sql语句执行
- jdbc连接mysql数据库执行sql语句ResultSet结果集一直为空
- 关于hibernate的session.createSQLQuery(sql)直接调用底层SQL后,返回结果集的问题