Spring HibernateTemplate与Callback

来源:互联网 发布:windows安装mac os x 编辑:程序博客网 时间:2024/05/17 14:27

    Spring中 Callback模式和Template模式合用 随处可见。下面以常用的HibernateTemplate为例进行简要简述。

     在HibernateTemplate模板类中有一个核心的方法:doExecute,这个核心的方法采用模板方法 完成相关的固定 操作(建立连接,执行操作,释放连接) ,其中的具体步骤通过回调传入的对象(这个对象就是实现了Callback接口的类)来完成。

一。HibernateTemplate类的使用:

    1。HibernateTemplate 类提供了大量方法对应Hibernate Session 接口中暴露的方法。使用这些方法时可以直接调用。save()方法使用例子:

Java代码  收藏代码
  1. import org.springframework.orm.hibernate3.HibernateTemplate;  
  2. @Component("userDaoImpl")  
  3. public class UserDaoImpl implements UserDao {  
  4.     private HibernateTemplate hibernateTemplate;  
  5.     public HibernateTemplate getHibernateTemplate()   
  6.     {  
  7.         return hibernateTemplate;  
  8.     }  
  9.   
  10.     @Resource(name="hibernatTemplate")  
  11.     public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {  
  12.         this.hibernateTemplate = hibernateTemplate;  
  13.     }  
  14.     @Override  
  15.     public void save(User u) {        
  16.         hibernateTemplate.save(u);  
  17.         //这里可以直接save了,session已经被hibernateTemplate处理了。我们不需要关心它了。  
  18.         //因为HibernateTemplate中已经注入了SessionFactory了,因为它自己会处理好session及其事务的。  
  19.         System.out.println("user save...");  
  20.     }}  

 因为HibernateTemplate需要交给Spring来管理,因为也需要配置bean,及注入sessionFactory:

Xml代码  收藏代码
  1. <bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">  
  2.     <property name="sessionFactory" ref="sessionFactory"/>  
  3. </bean>  

 

       2。当你需要使用的Session 方法没有在HibernateTemplate 中提供时,可以通过下面提供的基于回调的方案来实现,如下实例所示:

Java代码  收藏代码
  1. public class ProductDaoImpl implements ProductDao {  
  2.   
  3.     private HibernateTemplate hibernateTemplate;  
  4.   
  5.     public void setSessionFactory(SessionFactory sessionFactory) {  
  6.         this.hibernateTemplate = new HibernateTemplate(sessionFactory);  
  7.     }  
  8.   
  9.     public Collection loadProductsByCategory(final String category) throws DataAccessException {  
  10.         return this.hibernateTemplate.execute(new HibernateCallback() {  
  11.   
  12.             public Object doInHibernate(Session session) {  
  13.                 Criteria criteria = session.createCriteria(Product.class);  
  14.                 criteria.add(Expression.eq("category", category));  
  15.                 criteria.setMaxResults(6);  
  16.                 return criteria.list();  
  17.             }  
  18.         };  
  19.     }  
  20. }  

     3。HibernateDaoSupport 基类:

一个回调实现能够有效地在任何Hibernate数据访问中使用。HibernateTemplate 会确保当前Hibernate的 Session 实例的正确打开和关闭,并直接参与到事务管理中去。 Template实例不仅是线程安全的,同时它也是可重用的。因而他们可以作为外部对象的实例变量而被持有。对于那些简单的诸如find、load、saveOrUpdate或者delete操作的调用,HibernateTemplate 提供可选择的快捷函数来替换这种回调的实现。 不仅如此,Spring还提供了一个简便的 HibernateDaoSupport 基类,这个类提供了 setSessionFactory(..) 方法来接受一个 SessionFactory 对象,同时提供了 getSessionFactory() 和 getHibernateTemplate() 方法给子类使用。实例如下:

Java代码  收藏代码
  1. public class ProductDaoImpl extends HibernateDaoSupport implements ProductDao {  
  2.   
  3.     public Collection loadProductsByCategory(String category) throws DataAccessException {  
  4.         return this.getHibernateTemplate().find(  
  5.             "from test.Product product where product.category=?", category);  
  6.     }  
  7. }  

 4。HibernateDaoSupport 基类使用技巧:

  HibernateDaoSupport这也是使用HibernateTemplate的方法之一,只是需要dao继承这个类:

Java代码  收藏代码
  1. public class LogDaoImpl extends HibernateDaoSupport implements LogDao {  
  2.     @Override  
  3.     public void save(Log log) {       
  4.             this.getHibernateTemplate().save(log);  
  5.             //this.save(log);  
  6.         System.out.println("log save...");  
  7.     }  
  8. }  

     dao继承了HibernateDaoSupport类,而这个又拥有sessionFactory和hibernateTemplate的setXXX方法,只要我在初始化这个dao时,注入两个其中一个就可以了。但是这两个setXX方法都为final的,因为不可以重写,这样就不能使用annotation的方式了,只能使用xml的方法了。HibernateDaoSupport部分源代码如下:

Java代码  收藏代码
  1. public abstract class HibernateDaoSupport extends DaoSupport{  
  2.       private HibernateTemplate hibernateTemplate;  
  3.       
  4.      public final void setSessionFactory(SessionFactory sessionFactory) {  
  5.         //......  
  6.         }  
  7.     }  
  8.   
  9. public final void setHibernateTemplate(HibernateTemplate hibernateTemplate) {  
  10.         this.hibernateTemplate = hibernateTemplate;  
  11.     }  
  12.   
  13. public final HibernateTemplate getHibernateTemplate() {  
  14.       return this.hibernateTemplate;  
  15.     }  
  16. //.......其他方法  
  17. }  

 xml注入方法如下:

Java代码  收藏代码
  1. <bean id="logDaoImpl" class="example.dao.impl.LogDaoImpl">  
  2.     <property name="hibernateTemplate" ref="hibernateTemplate"></property>  
  3.   </bean>  

 但是问题又来了,如果dao有多个(甚至几个以上),这样就会有大量的xml配置文件,每个dao需要一个配置一个bean,并且在这个bean中需要注入一个sessionFactory或是hibernateTemplate,这样的工作量是非常的大,并且容易出错。
针对有大量的dao我们提供一个解决方案:
我们可以创建一个类,并且这个类来继承hibernateDaoSupport这个类,由于hibernateDaoSupport类的sessionFactory和hibernateTemplate的setXXX方法是final的,因此不能重写,但我们可以在这个类中注入一个sessionFactory或hibernateTemplate但是setXXX方法名,用其它的。然后让dao来继承这个类,这就dao就可以使用annotation方式注解了。代码如下:

Java代码  收藏代码
  1. @Component  
  2. public class SuperDao extends HibernateDaoSupport {  
  3.     @Resource(name="hibernateTemplate")  
  4.     public void setSuperHibernateTemplate(HibernateTemplate hibernateTemplate ){  
  5.         //在这里为父类HibernateDaoSuppport注入hibernateTemplate或是sessionFactory  
  6.         super.setHibernateTemplate(hibernateTemplate);  
  7.     }  
  8. }  

 其它的dao继承这个就可以了,如下所示:

Java代码  收藏代码
  1. @Component  
  2. public class LogDaoImpl extends SuperDao implements LogDao {  
  3.     @Override  
  4.     public void save(Log log) {  
  5.             this.getHibernateTemplate().save(log);  
  6.             //this.save(log);  
  7.         System.out.println("log save...");  
  8.     }  
  9. }  
 

其他的用法不在这里累述,请参看Spring的参考文档.下面重点说说Spring HibernateTemplate模板方法与Callback机制.

二。HibernateTemplate源码分析

下面以save()方法为例进行说明,先看源代码:

HibernateCallback接口的代码如下, 它只有一个方法doInHibernate方法:

Java代码  收藏代码
  1. public interface HibernateCallback {  
  2.        Object doInHibernate(Session session) throws HibernateException, SQLException;  
  3. }  

 HibernateTemplate中的具体操作的方法,如save(),update()的具体实现都采用匿名类的方式实现了该接口,在doInHibernate中完成具体的操作。以save()方法为例:

Java代码  收藏代码
  1. public Serializable save(final Object entity) throws DataAccessException {  
  2.         return (Serializable) executeWithNativeSession(new HibernateCallback() {  
  3.             public Object doInHibernate(Session session) throws HibernateException {  
  4.                 checkWriteOperationAllowed(session);  
  5.                 return session.save(entity);  
  6.             }  
  7.         });  
  8.     }  

 这段代码重点注意以匿名类的方式实现了 HibernateCallback接口.

然后是executeWithNativeSession()方法如下,在此方法中调用核心方法doExecute,如下所示:

Java代码  收藏代码
  1. public Object executeWithNativeSession(HibernateCallback action) {  
  2.         return doExecute(action, falsetrue);  
  3.     }  

 在这个方法中使用本地已存在的Session去执行此次save操作,doExecute代码如下所示:

Java代码  收藏代码
  1. /** 
  2.      * Execute the action specified by the given action object within a Session. 
  3.      * @param action callback object that specifies the Hibernate action 
  4.      * @param enforceNewSession whether to enforce a new Session for this template 
  5.      * even if there is a pre-bound transactional Session 
  6.      * @param enforceNativeSession whether to enforce exposure of the native 
  7.      * Hibernate Session to callback code 
  8.      * @return a result object returned by the action, or <code>null</code> 
  9.      * @throws org.springframework.dao.DataAccessException in case of Hibernate errors 
  10.      */  
  11.     protected Object doExecute(HibernateCallback action, boolean enforceNewSession, boolean enforceNativeSession)  
  12.             throws DataAccessException {  
  13.   
  14.         Assert.notNull(action, "Callback object must not be null");  
  15.   
  16.         Session session = (enforceNewSession ?  
  17.                 SessionFactoryUtils.getNewSession(getSessionFactory(), getEntityInterceptor()) : getSession());  
  18.         boolean existingTransaction = (!enforceNewSession &&  
  19.                 (!isAllowCreate() || SessionFactoryUtils.isSessionTransactional(session, getSessionFactory())));  
  20.         if (existingTransaction) {  
  21.             logger.debug("Found thread-bound Session for HibernateTemplate");  
  22.         }  
  23.   
  24.         FlushMode previousFlushMode = null;  
  25.         try {  
  26.             previousFlushMode = applyFlushMode(session, existingTransaction);  
  27.             enableFilters(session);  
  28.             Session sessionToExpose =  
  29.                     (enforceNativeSession || isExposeNativeSession() ? session : createSessionProxy(session));  
  30.             Object result = action.doInHibernate(sessionToExpose);  
  31.             flushIfNecessary(session, existingTransaction);  
  32.             return result;  
  33.         }  
  34.         catch (HibernateException ex) {  
  35.             throw convertHibernateAccessException(ex);  
  36.         }  
  37.         catch (SQLException ex) {  
  38.             throw convertJdbcAccessException(ex);  
  39.         }  
  40.         catch (RuntimeException ex) {  
  41.             // Callback code threw application exception...  
  42.             throw ex;  
  43.         }  
  44.         finally {  
  45.             if (existingTransaction) {  
  46.                 logger.debug("Not closing pre-bound Hibernate Session after HibernateTemplate");  
  47.                 disableFilters(session);  
  48.                 if (previousFlushMode != null) {  
  49.                     session.setFlushMode(previousFlushMode);  
  50.                 }  
  51.             }  
  52.             else {  
  53.                 // Never use deferred close for an explicitly new Session.  
  54.                 if (isAlwaysUseNewSession()) {  
  55.                     SessionFactoryUtils.closeSession(session);  
  56.                 }  
  57.                 else {  
  58.                     SessionFactoryUtils.closeSessionOrRegisterDeferredClose(session, getSessionFactory());  
  59.                 }  
  60.             }  
  61.         }  
  62.     }  

 doExecute这个方法有点过于复杂,不容易理解,现在我用差不多是伪代码的形式来把doExecute这个方法的大概意思写出为,以便大家理解:

Java代码  收藏代码
  1. public class MyHibernateTemplate {  
  2.   
  3.     public Object executeWithNativeSession(HibernateCallback action) {  
  4.         return doExecute(action, falsetrue);  
  5.     }  
  6.       
  7.     protected Object doExecute(HibernateCallback action, boolean enforceNewSession, boolean enforceNativeSession){  
  8.         Session session = null;  
  9.         try {  
  10.             session = getSession();  
  11.             session.beginTransaction();  
  12.             action.doInHibernate(session);  
  13.             session.getTransaction().commit();  
  14.         } catch (Exception e) {  
  15.             session.getTransaction().rollback();  
  16.         }finally{  
  17.             //....进行额外处理  
  18.         }  
  19.         return null;  
  20.     }  
  21.   
  22.     private Session getSession() {  
  23.         // TODO Auto-generated method stub  
  24.         return new Session();  
  25.     }  
  26. }  
 

如果你觉得使用匿名内部类传参的方式不好理解,你可以先写一个特定针对save操作的实现类来帮助理解它,如下所示:

Java代码  收藏代码
  1. public class HibernateSaveCallbackImpl implements HibernateCallback {  
  2.   
  3.     private Object object ;  
  4.     public HibernateSaveCallbackImpl(){}  
  5.     public HibernateSaveCallbackImpl(Object object){  
  6.         this.object = object;  
  7.     }  
  8.     public Object doInHibernate(Session session) throws HibernateException,  
  9.             SQLException {  
  10.         session.save(object);  
  11.         return null;  
  12.     }  
  13. }  

 然后再实例化一个HibernateSaveCallbackImpl对象传给MyHibernateTemplate进行使用,如下所示:

Java代码  收藏代码
  1. public static void main(String[] args) {  
  2.         HibernateCallback callback = new HibernateSaveCallbackImpl();  
  3.         HibernateCallback callback2 = new HibernateSaveCallbackImpl("save this object");  
  4.         new MyHibernateTemplate().executeWithNativeSession(callback);  
  5.     }  

 当MyHibernateTemplate中的doExecute方法执行时,执行到

Java代码  收藏代码
  1. action.doInHibernate(session);  

时,它会先去执行你传入的引用(也就是你自己)的方法。所以称为回调,这与模板模式中的钩子函数基本是一样的。其实回调,简单的可以说是:将自己的引用传给别的方法,在别的方法里面,通过自己的引用调用自己的方法。


转载:http://ryxxlong.iteye.com/blog/749420

0 0