HibernateTemplate还提供一种更加灵活的方式来操作数据库,通过这种方式可以完全使用Hibernate的操作方式。HibernateTemplate的灵活访问方式是通过如下两个方法完成:
Object execute(HibernateCallback action)
List execute(HibernateCallback action)
这两个方法都需要一个HibernateCallback的实例,HibernateCallback实例可在任何有效的Hibernate数据访问中使用。程序开发者通过HibernateCallback,可以完全使用Hibernate灵活的方式来访问数据库,解决Spring封装Hibernate后灵活性不足的缺陷。HibernateCallback是一个接口,该接口只有一个方法doInHibernate(org.hibernate.Session session),该方法只有一个参数Session。在开发中提供HibernateCallback实现类时,必须实现接口里包含的 doInHibernate方法,在该方法体内即可获得Hibernate Session的引用,一旦获得了Hibernate Session的引用,就可以完全以Hibernate的方式进行数据库访问。
注意:方法doInHibernate方法内可以访问Session,该Session对象是绑定到该线程的Session实例。该方法内的持久层操作,与不使用Spring时的持久层操作完全相同。这保证对于复杂的持久层访问,依然可以使用Hibernate的访问方式。
通常,程序中采用实现HibernateCallback的匿名内部类来获取HibernateCallback的实例,方法doInHibernate的方法体就是Spring执行的持久化操作。具体代码如下:
public class PersonDaoImpl implements PersonDao
{
private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sessionFactory){
this.sessionFactory = sessionFactory;
}
/**
*
* 通过人名查找所有匹配该名的Person实例
*
* @param name
* 匹配的人名
*
* @return 匹配该任命的全部Person集合
*
*/
public List findPersonsByName(final String name){
HibernateTemplate hibernateTemplate =new HibernateTemplate(this.sessionFactory); // 创建HibernateTemplate实例
return (List) hibernateTemplate.execute( // 返回HibernateTemplate的execute的结果
// 创建匿名内部类
new HibernateCallback(){
public Object doInHibernate(Session session) throws HibernateException{
List result = session.createCriteria(Person.class).add(Restrictions.like("name", name+"%").list();
return result;
}
});
}
}
---------------------------------------------------------------
Spring 对 hibernate 的集成(使用回调callback)
比如在删除一条在数据库操作的时候 我们一般是类似是这样使用:
this.getHibernateTemplate().delete("from Information where INFOID='"+infoid.trim()+"'");
然而具体在spring内部是怎么操作的呢?
delete()----->excecute()----->执行回调方法HibernateCallback .doInHibernate()。
下面来让我们来直接看一下spring的源代码。
//hibernate回调接口
public interface HibernateCallback {
Object doInHibernate(Session session) throws HibernateException, SQLException;
}
//...
package org.springframework.orm.hibernate;
public class HibernateTemplate extends HibernateAccessor implements HibernateOperations {
//...
public int delete(final String queryString) throws DataAccessException {
Integer deleteCount = (Integer) execute(new HibernateCallback() {//定义回调实现
public Object doInHibernate(Session session) throws HibernateException {
checkWriteOperationAllowed(session);
return new Integer(session.delete(queryString));//此处有hibernate的实现操作
}
});
return deleteCount.intValue();
}
public Object execute(HibernateCallback action) throws DataAccessException {
Session session = (!isAllowCreate() ? SessionFactoryUtils.getSession(getSessionFactory(), false) :
SessionFactoryUtils.getSession(getSessionFactory(), getEntityInterceptor(), getJdbcExceptionTranslator()));
boolean existingTransaction = TransactionSynchronizationManager.hasResource(getSessionFactory());
if (!existingTransaction && getFlushMode() == FLUSH_NEVER) {
session.setFlushMode(FlushMode.NEVER);
}
try {
Object result = action.doInHibernate(session);//此处调用hibernatecallback回调接口即hibernate的实现
flushIfNecessary(session, existingTransaction);
return result;
}
catch (HibernateException ex) {
throw convertHibernateAccessException(ex);
}
catch (SQLException ex) {
throw convertJdbcAccessException(ex);
}
catch (RuntimeException ex) {
// callback code threw application exception
throw ex;
}
finally {
SessionFactoryUtils.closeSessionIfNecessary(session, getSessionFactory());
}
}
//...
//其他操作类似
}
--------------------------------------------------------
Hibernate中运用doInHibernate 构造公用findByhql和updateByHql方法
在hibernate中,运用doInHibernate,避免了手动open session和close session ,减少了一系列session关闭的麻烦。
公用的setParameter 方法,下面有调用,传入一个query 实例,和Map<String, Object> map 类型的参数
private void setParameter(Query query, Map<String, Object> map) {
if (map != null && !map.isEmpty()) {
Set<String> keySet = map.keySet();
for (String string : keySet) {
Object obj = map.get(string);
// 这里考虑传入的参数是什么类型,不同类型使用的方法不同
if (obj instanceof Collection<?>) {
query.setParameterList(string, (Collection<?>) obj);
} else if (obj instanceof Object[]) {
query.setParameterList(string, (Object[]) obj);
} else {
query.setParameter(string, obj);
}
}
}
}
public void updateByHql(final String hql, final Map<String, Object> map) throws Exception {
template.execute(new HibernateCallback() {
@Override
public Object doInHibernate(Session session) throws HibernateException, SQLException {
Query query = session.createQuery(hql);
setParameter(query, map); //调用set参数的方法
query.executeUpdate();
return null;
}
});
}
public List<Object> findByHql(final String hql, final Map<String, Object> map) throws Exception {
return (List<Object>) template.execute(new HibernateCallback() {
public List<Object> doInHibernate(final Session session) throws HibernateException, SQLException {
Query query = session.createQuery(hql);
setParameter(query, map);
return query.list();
}
});
}
调用例子:
String hql = "from ABC where bb = :aa";
Map<String, Object> map = new HashMap<String, Object>();
map.put("aa", 22);
List<Object> cList = commonDao.findByHql(hql, map);
if (cList == null || cList.size() == 0)
return null;
else
return (ABC) cList.get(0);
update类似与query,只是没有返回参数
--------------------------------------------------------
下面的代码对HibernateDaoSupport类进行扩展(虽然Spring 2.0的HibernateTemplate提供了一个分页方法setMaxResults,但仅此一个方法依然不能实现分页查询),这种扩展主要是为该类增加了3个分页查询的方法,分页查询时必须直接调用Hibernate的Session完成,因此,必须借助于HibernateCallBack的帮助。
public class YeekuHibernateDaoSupport extends HibernateDaoSupport
{
/**
* 使用hql 语句进行分页查询操作
* @param hql 需要查询的hql语句
* @param offset 第一条记录索引
* @param pageSize 每页需要显示的记录数
* @return 当前页的所有记录
*/
public List findByPage(final String hql, final int offset, final int pageSize)
{
//HibernateDaoSupport已经包含了getHibernateTemplate()方法
List list = getHibernateTemplate().executeFind(new
HibernateCallback()
{
public Object doInHibernate(Session session) throws HibernateException, SQLException
//该方法体内以Hibernate方法进行持久层访问
{
List result = session.createQuery(hql) .setFirstResult(offset) .setMaxResults(pageSize) .list();
return result;
}
});
return list;
}
/**
* 使用hql 语句进行分页查询操作
* @param hql 需要查询的hql语句
* @param value 如果hql有一个参数需要传入,value就是传入的参数
* @param offset 第一条记录索引
* @param pageSize 每页需要显示的记录数
* @return 当前页的所有记录
*/
public List findByPage(final String hql , final Object value , final int offset, final int pageSize)
{
List list = getHibernateTemplate().executeFind(new
HibernateCallback()
{
public Object doInHibernate(Session session) throws HibernateException, SQLException
{
//下面查询的是最简单的Hiberante HQL查询
List result = session.createQuery(hql) .setParameter(0, value) .setFirstResult(offset) .setMaxResults(pageSize) .list();
return result;
}
});
return list;
}
/**
* 使用hql 语句进行分页查询操作
* @param hql 需要查询的hql语句
* @param values 如果hql有多个参数需要传入,values就是传入的参数数组
* @param offset 第一条记录索引
* @param pageSize 每页需要显示的记录数
* @return 当前页的所有记录
*/
public List findByPage(final String hql, final Object[] values, final int offset, final int pageSize)
{
List list = getHibernateTemplate().executeFind(new
HibernateCallback()
{
public Object doInHibernate(Session session) throws HibernateException, SQLException
{
Query query = session.createQuery(hql);
for (int i = 0 ; i < values.length ; i++)
{
query.setParameter( i, values[i]);
}
List result = query.setFirstResult(offset) .setMaxResults(pageSize) .list();
return result;
}
});
return list;
}
}
在上面的代码实现中,直接使用了getHibernateTemplate()方法,这个方法由Hibernate- DaoSupport提供。而YeekuHibernateDaoSupport是HibernateDaoSupport的子类,因此,可以直接使用该方法。
当实现doInHibernate(Session session)方法时,完全以Hibernate的方式进行数据库访问,这样保证了Hibernate进行数据库访问的灵活性。
注意:Spring提供的XxxTemplate和XxxCallBack互为补充,二者体现了Spring框架设计的用心良苦:XxxTemplate对通用操作进行封装,而XxxCallBack解决了封装后灵活性不足的缺陷。
--------------------------------------------------------
Spring和Hibernate集成的HibernateTemplate的一些常用方法总结
1:get/load存取单条数据
public Teacher getTeacherById(Long id) {
return (Teacher)this.hibernateTemplate.get(Teacher.class, id);
}
public Teacher getTeacherById(Long id) {
return (Teacher)this.hibernateTemplate.load(Teacher.class, id);
}
2:find/iterate查询操作
public Iterator getTeachersByAge(int age) {
Iterator iterator = null;
//使用find方法
List list = (List)this.hibernateTemplate().find("from Teacher t where t.age>?", new Integer(age));
iterator = list.iterator();
//使用iterator方法
iterator = this.hibernateTemplate().iterate("from Teacher t where t.age>?", new Integer(age));
return iterator;
}
find和iterato的区别主要是iterate采用了N+1次查询,对于大批量查询,比如查询10000条记录,那么iterate就要执行10000+1次查询,find和iterate应根据具体的实际
情况来使用,对于频繁的写操作对象,应使用find查询,而对于一些只读的数据对象,应使用iterate操作,因为iterate操作使用了Hibernate的缓存机制
3:save/update/saveOrUpdate/delete 保存/更新/删除操作
public void save(Teacher teacher) {
this.hibernateTemplate.save(teacher);
}
public void update(Teacher teacher) {
this.hibernateTemplate.update(teacher);
}
public void update(Teacher teacher) {
this.hibernateTemplate.saveOrUpdate(teacher);
}
public void update(Teacher teacher) {
this.hibernateTemplate.delete(teacher);
}
4:bulkUpdate批量删除或者更新
bulkUpdate提供了批量删除和更新,直接转换为相应的update/delete SQL进行批量删除和更新
public void batchDelete(String name, int age) {
this.hibernateTemplate.bulkUpdate("delete Teacher where name=? and age = ?", new Object[]{name, age});
}
public void batchUpdate(String name, String newName) {
this.hibernateTemplate.bulkUpdate("update Teacher set name=? where name=?", new Object[]{newName, name});
}
此时要注意的一个问题是,使用bulkUpdate操作,必须手工清除相关对象在Hibernate中的缓存(包括一级缓存和二级缓存)
5:execute核心方法
public Object execute(HibernateCallBack action, boolean exposeNativeSession) throws DataAccessException {
Session session = getSession(); //获取一个Session
boolean existingTransaction = SessionFactoryUtils.isSessionTransactional(session, getSessionFactory()); //当前session是否在事务中
FlushMode previousFlushMode = null;
try {
previousFlushMode = applyFlushMode(session, existingTransaction); //应用flush模式
enableFilters(session);
Session sessionToExpose = (exposeNativeSession? session: createSessionProxy(session)); //暴露给action的session
Object result = action.doInHibernate(sessionToExpose); //执行action
flushIfNecessary(session, existingTransaction);
return result;
} catch(HibernateException ex) {
throw convertHibernateAccessException(ex);
} catch(SQLException ex) {
throw convertJdbcAccessException(ex);
} catch(RuntimeException ex) {
throw ex;
} finally {
if(existingTransaction) { //如果session在事务中,则不关闭session
disableFilters(session);
if(previousFlushMode != null) {
session.setFlushMode(previousFlushMode);
}
} else {
SessionFactoryUtils.releaseSession(session, getSessionFactory()); //释放session
}
}
}
*HibernateCallBack,一般用来实现特定的业务逻辑
*exposeNativeSession:是一个布尔值,要暴露给HibernateCallBack实际的session对象,而不是一个代理过的对象
6:一般情况下,只有HIberateTemplate提供的方法不能满足要求时才使用execute方法,它的使用情况如下所示,
public void createDatabaseSchema() throws DataAccessException {
HibernateTemplate hibernateTemplate = new HibernateTemplate(this.sessionFactory);
//调用HibernateTempalte的execute方法
hibernateTemplate.execute(new HibernateCallback() {
public Object doInHibernate(Session session) throws HibernateException, SQLException { //实现HibernateCallback的doInHibernate方法
//具体实现
Connection conn = session.connection();
final Dialect dialect = Dialect.getDialect(configuration.getProperties);
String[] sql = configuration.generateSchemaCreationScript(dialect);
executeSchemaScript(conn, sql);
}
});
}
使用execute方法的重点是实现HibernateCallback的doInHibernate方法,它会传递一个Session实例,可以使用此Session实例操作数据库,由此看出execute方法的好处是应用程序不用关心session的创建和释放,只需要处理关心的业务逻辑即可。
--------------------------------------------------------
ref:
http://www.blogjava.net/rain1102/articles/170638.html
http://blog.csdn.net/changewang/article/details/575191
http://yunqiang-zhang-hotmail-com.iteye.com/blog/1973010
http://www.linuxidc.com/Linux/2011-12/48719.htm