Spring整合Hibernate的时候,Spring内部提供了对Hibernate的封装,也就是HibernateTemplate、HibernateDaoSupport这两个类,以前自己写Hibernate来操作数据库的时候,我们要自己在代码中先取得SessionFactory,然后在取得Session,然后在用Session操作数据库,最后还要自己处理事务的提交,或者事务的回滚,以及数据库连接的关闭。下面讲解如何在整合Spring+Hibernate的时候使用这两个Spring中的类来简化我们的操作。
(这里的代码基于此篇文章中的代码来改变:Spring学习_06_Spring中事物属性(XML方式)重要)
首先讲解如何使用HibernateTemplate来简化DAO层的操作, 严格说来,HibernateTemplate是一种设计模式,这种模式叫做模板方法模式。注意,使用HibernateTemplate的方式是采用组合的方式。如下几步即可完成。
第一步:在beans.xml中让Spring管理我们的HiberanteTemplate的实例:
<bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
由于我们使用这个HibernateTempate的时候,是不需要自己去取得SessionFactory、Session等Bean的,所以这里我们理应将SessionFactory注入给它,让HibenateTemplate为我们工作的时候知道如何去取得SessionFactory,继而取得Session。
第二部:在我们的代码中将前面的自己取得SessionFactory,Session的方式改为如下所示:
@Component("userDao")
public class UserDAOImpl implements UserDAO {
HibernateTemplate hibernateTemplate;
public HibernateTemplate getHibernateTemplate() {
return hibernateTemplate;
}
@Resource
public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
this.hibernateTemplate = hibernateTemplate;
}
public void save(User user) {
hibernateTemplate.save(user);
//throw new RuntimeException("Error");
}
}
完成以上两步,即可以正常使用HibernateTemplate,看看,原本需要些一大段代码来存储User对象的,现在一句话搞定!是不是很方便?
现在,我们需要探讨一下:hibernateTemplate.save(user);这句话里面到底做了什么?我们查看源代码发现这个save方法其实有调用了一个ExecuteWithNativeSession方法,同时传入了以HibernateCallBack的匿名实现类,这就是“回调”,这样我们在执行ExecuteWithNativeSession的中间某个地方的时候可以调用这个HibernateCallBack类的任何方法,而且还可以传递相应的Session对象来完成特定的实际的数据库操作,而在ExecuteWithNativeSession方法的前半部分和后半部分就可以一些操作数据的特定操作,比如打开Sesison、关闭Session。这里的传入的HibernateCallBack就是回调类,也就是我们在Windows平台中所说的钩子类,里面的方法就叫做钩子方法,钩子上挂什么就执行什么。这在window上很常用,比如键盘的监听程序,里面放置很多钩子,按下一个什么按键的时候,某个钩子函数就去执行某种操作。
下面讲解一下:如何使用HibernateDaoSupport来简化DAO层的操作。注意,使用HiberanteDaoSupport的方式使用的是继承!采用如下几步即可完成:
第一步:由于一般我们让数据库的Dao层去继承HibernateDaoSupport,所以我们做一个抽象类来继承HibernateDaoSupport,然后让我们的Dao层去继承这个抽象类,从而间接的继承HibernateDaoSupport。后面会讲解原因,抽象类的代码如下所示:
@Component("superDao")
public abstract class SuperDao extends HibernateDaoSupport{
@Resource
public void setSuperSessionFactory(SessionFactory sessionFactory){
super.setSessionFactory(sessionFactory);
}
}
第二步:做我们自己的Dao层,这次我们以MsgDaoImple为例:代码如下:
@Component("msgDao")
public class MsgDAOImpl extends SuperDao implements MsgDAO{
public void save(Msg msg) {
this.getHibernateTemplate().save(msg);
//this.save(msg);//用他会出现:StackOverflowError错误。内部估计是出现了循环
}
}
经过测试,运行良好。我们还发现其实HibernateDaoSupport其实就是包裹了一个HibernateTemplate。
这里重点说一下,上面为什么我们需要先制作一个抽象类。首相我们想象一下如果不做一个抽象类,那么我们的MsgDAOImpl就要继承HibernateDaoSupport,我们知道使用HibernateDaoSupport同样也需要注入SessionFacotry,或者是注入一个HibernateTemplate。但是我们发现由于HibernateDaoSupport中的方法全部是final标示的,这样就造成了子类不可以继承其中的setSessionFactory和setHibernateTemplate方法,也就没有办法通过Annotation的方式为其注入所需要的HibernateTemplate后者是SessionFactory。所以只好在XML中配置,这样假设有五百个Dao,那想一下是不是要在XML中配置五百次,是不是很累? 那很多人会问,我们将HibernateDaoSupport配置在XML中,然后子类继承HibernateDaoSupport的时候使用Annotation来实例化不OK了吗?但是其实经过测试我们发现一个问题就是:如果子类和父类采用不同方式来实例化(比如父类用XML方式 ,子类使用Annotation的方式),这样是会出现问题的,因为混合使用这两种方式来实例化一个对象会造成类型的不匹配。所以我们上面通过使用一个抽象类类作为一个中间层,我们在中间层使用一个“小伎俩”来套结这两种方式:我们使用一个不规范的方法来取得Spring容器中的一个Sessionfory对象,同时将其用调用方法的方式注入给父类对象,通过这种曲线救国的方式来将sessionFactory注入给我们的HibernateDaoSupport。这样我们就不需要在xml中独立配置HibernateDaoSupport,而且还可以将一个sessionFactory后者HibernateTemplate赋值给它。一举两得!