hibernateTemplate详解

    hibernateTemplate为简化Hibernate数据访问代码的辅助类。以下会由浅到深一步步介绍

一、基本定义

  1. /*
  2. org.springframework.orm.hibernate3
  3. 类 HibernateTemplate
  4. java.lang.Object
  5. org.springframework.orm.hibernate3.HibernateAccessor
  6. org.springframework.orm.hibernate3.HibernateTemplate
  7. 所有已实现的接口:
  8. HibernateOperations
  9. */
  10. public class HibernateTemplate extends HibernateAccessor implements HibernateOperations

英文备注:
    1、Helper class that simplifies Hibernate data access code, and converts checked HibernateExceptions into unchecked DataAccessExceptions, following the org.springframework.dao exception hierarchy. Uses the same SQLExceptionTranslator mechanism as JdbcTemplate.
    2、Typically used to implement data access or business logic services that use Hibernate within their implementation but are Hibernate-agnostic in their interface. The latter or code calling the latter only have to deal with domain objects, query objects, and org.springframework.dao exceptions.
    3、The central method is execute, supporting Hibernate code implementing the HibernateCallback interface. It provides Hibernate Session handling such that neither the HibernateCallback implementation nor the calling code needs to explicitly care about retrieving/closing Hibernate Sessions, or handling Session lifecycle exceptions. For typical single step actions, there are various convenience methods (find, load, saveOrUpdate, delete).
    4、Can be used within a service implementation via direct instantiation with a SessionFactory reference, or get prepared in an application context and given to services as bean reference. Note: The SessionFactory should always be configured as bean in the application context, in the first case given to the service directly, in the second case to the prepared template.
    5、This class can be considered as direct alternative to working with the raw Hibernate3 Session API (through SessionFactory.getCurrentSession()). The major advantage is its automatic conversion to DataAccessExceptions, the major disadvantage that no checked application exceptions can get thrown from within data access code. Corresponding checks and the actual throwing of such exceptions can often be deferred to after callback execution, though.
    6、Note that even if HibernateTransactionManager is used for transaction demarcation in higher-level services, all those services above the data access layer don't need need to be Hibernate-aware. Setting such a special PlatformTransactionManager is a configuration issue: For example, switching to JTA is just a matter of Spring configuration (use JtaTransactionManager instead) that does not affect application code.
    7、LocalSessionFactoryBean is the preferred way of obtaining a reference to a specific Hibernate SessionFactory, at least in a non-EJB environment. Alternatively, use a JndiObjectFactoryBean to fetch a SessionFactory from JNDI (possibly set up via a JCA Connector).
    8、Note that operations that return an Iterator (i.e. iterate) are supposed to be used within Spring-driven or JTA-driven transactions (with HibernateTransactionManager, JtaTransactionManager, or EJB CMT). Else, the Iterator won't be able to read results from its ResultSet anymore, as the underlying Hibernate Session will already have been closed.
    9、Lazy loading will also just work with an open Hibernate Session, either within a transaction or within OpenSessionInViewFilter/Interceptor. Furthermore, some operations just make sense within transactions, for example: contains, evict, lock, flush, clear.

    简化一下,大概意思是:
    1、简化Hibernate数据访问代码的辅助类。自动转换Hibernate原生异常为符合org.springframework.dao异常体系的DataAccessExceptions异常。
    3、最重要的是execute方法支持实现了HibernateCallback接口的Hibernate访问代码。无论是HibernateCallback接口实现或者其他的调用代码,无需关心对Session对象的获取和关闭以及Session对象的生命期异常。对典型的单步骤调用,提供了多种便利方法(find, load, saveOrUpdate, delete)。
    4、在一个service实现中可以通过一个SessionFactory引用直接获得该实例,或者在一个应用的上下文环境中获得一个配置好的实例作为该service的内部引用。注意:SessionFactory应当永远以bean的形式被配置在应用的上下文当中,第一种情况下直接提供给service实现,第二种情况提供模板实例引用。
    5、可以通过SessionFactory.getCurrentSession()达到使用原生Hibernate3的效果,主要优势是其自动转换为 DataAccessExceptions,最大的缺点,没有选中的应用程序可以得到异常从内数据访问代码。相应的检查和实际投掷的此类异常可以经常推迟到回调执行后。
    9、延迟加载也只是使用已经存在的Hibernate Session,都是在一个事务内或在OpenSessionInViewFilter/Interceptor。此外,某些操作只在实务中进行,例如: contains, evict, lock, flush, clear。

    HibernateTemplate 可将Hibernate的持久层访问模板化,使用HibernateTemplate非常简单。从父类继承的字段有:FLUSH_ALWAYS, FLUSH_AUTO, FLUSH_COMMIT, FLUSH_EAGER, FLUSH_NEVER, logger。

    下面会简单介绍它的构造器和方法,若需具体了解详细说明,请自行百度(学习得靠自己!)。

二、构造器概要

    /*构造器和说明*///Create a new HibernateTemplate instance
HibernateTemplate()
    /*sessionFactory - SessionFactory to create Sessions*/
HibernateTemplate(SessionFactory sessionFactory)
    /*
    sessionFactory - SessionFactory to create Sessions
    allowCreate - if a non-transactional Session should be created when no transactional Session can be found for the current thread
    */
HibernateTemplate(SessionFactory sessionFactory, boolean allowCreate)

三、方法概要

    

    可以看出,hibernateTemplate提供了非常强大的方法支持。
    方法具体用法就不详细解释了,其实用来起是很简单的,在后面的使用,会简单的涉及到一下方法的运用。

四、使用

    1、Spring+Hibernate(+SpringMVC)时候的使用

    相信这里的环境大家都是会配的,如果不清楚,请看博客中框架环境配置相关的文章。
    直接切入正题吧,配置多是使用的注解。

    直接在spring配置文件中集成的hibernate配置(applicationContext.xml),如果需要配置连接多个数据库,即按照下面的模板多写一个配置即可:

<context:property-placeholder location="classpath:config/jdbc.properties" />
    <bean id="dataSourceMysql" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close">
        <property name="driverClassName" value="${jdbc.mysql.driverClassName}" />
        <property name="url" value="${jdbc.mysql.url}" />
        <property name="username" value="${jdbc.mysql.username}" />
        <property name="password" value="${jdbc.mysql.password}" />
        <property name="maxActive" value="100"></property>
        <property name="minIdle" value="20"></property>
        <property name="maxWait" value="3000"></property>
        <property name="initialSize" value="30"></property>
    </bean>
    <bean id="sessionFactoryMysql"   class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
        <property name="dataSource" ref="dataSourceMysql" />
        <property name="packagesToScan">
            <list>
                <value>com.anxpp.demo.core.entity.mysql</value>
            </list>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                <prop key="hibernate.show_sql">false</prop>
                <prop key="hibernate.format_sql">true</prop>
                <prop key="hibernate.hbm2ddl.auto">update</prop> 
            </props>
        </property>
    </bean>

     数据库连接配置(jdbc.properties):

#Oracle
#jdbc.driverClassName=oracle.jdbc.driver.OracleDriver
#jdbc.url=jdbc:oracle:thin:@地址:端口:数据库 #jdbc.username=**** #jdbc.password=**** # #mysql jdbc.mysql.driverClassName=com.mysql.jdbc.Driver
jdbc.mysql.url=jdbc:mysql://数据库地址:端口/对应数据库 jdbc.mysql.username=**** jdbc.mysql.password=**** # #sql server
jdbc.sqlserver.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver jdbc.sqlserver.url=jdbc:sqlserver://数据库地址:端口;DatabaseName=数据库名字 jdbc.sqlserver.username=**** jdbc.sqlserver.password=****

     dao:

public interface ERPDao {
    void saveToSqlserver(Object entity);
}

    daoImpl:

@Repository
public class ERPDaoImpl implements ERPDao {
    @Autowired
    protected HibernateTemplate hibernateTemplateMysql;
    @Autowired
    protected HibernateTemplate hibernateTemplateSqlserver;
    /**  * 存入sqlServer   */
    @Override
    public void saveToSqlserver(Object entity) {
        hibernateTemplateSqlserver.save(entity);
    }

     配置两个数据库使用也是很方便的,就想上面一样,然后再service中便可以调用了。

    hql查询

    @Override
    public List<UserOnline> getUserOnlines(long startTime,long endTime) {
        List<UserOnline> userOnlines = new ArrayList<>();
        String hql = "SELECT * FROM ERPSingleHospital H WHERE H.currentTime>? AND H.currentTime<?";
        List<?> result = hibernateTemplateMysql.find(hql,startTime,endTime);       
        Iterator<?> it = result.iterator();
        while (it.hasNext()) {
            Object[] tuple = (Object[]) it.next();
            userOnlines.add(new UserOnline((long)tuple[0],(String)tuple[1],(double)tuple[2]));
        }   
        return userOnlines;
    }

    这里用到的函数:List HibernateTemplate.find(String queryString, Object... values) throws DataAccessException
    使用起来也是特别简单,“Object... values”为可变参数,如果不清楚,可以度娘或者认真看看《Java编程思想》,java基础功底够深厚,什么框架都能很快上手!queryString为查询语句,调用方式为find(hql,value1,value2,....);value为查询条件,有多少就传多少。调用会返回一个java.util.List,通过迭代器取值即可。

    mysql存储过程的调用:

@Override
    public List<DoctorOnlineDetail> getDoctorOnlineDetails(final long startTime,final long endTime) {
        return hibernateTemplateMysql.execute(new HibernateCallback() {
            @SuppressWarnings("deprecation")
            public Object doInHibernate(Session session) throws HibernateException,SQLException {
                List<DoctorOnlineDetail> doctorOnlines = new ArrayList<>();
                //存储过程调用语句
                String procdure = "{Call pro_doctor_online_detail(?,?)}";  
                CallableStatement cs;
                cs = session.connection().prepareCall(procdure);
                //参数设置
                cs.setLong(1, startTime);
                cs.setLong(2, endTime);
                //执行调用
                ResultSet rs = cs.executeQuery();
                //迭代取值(也可返回rs后处理)
                while(rs.next()){
                    doctorOnlines.add(new DoctorOnlineDetail(rs.getString(1),rs.getString(2),rs.getString(3),rs.getString(4),rs.getLong(5),rs.getInt(6),rs.getInt(7)));
                }
                return doctorOnlines;
            }
        });
    }

    存储过程的调用要用到Session,通过hibernateTemplate的execute(new HibernateCallback())方法即可实现,这里,无需关心对Session对象的获取和关闭以及Session对象的生命期异常。自己实现HibernateCallback接口,通过Session可以调用Hibernate API,对存储过程的调用,见代码即可,其中,也可以把遍历取值的代码放到execute方法外,性能应该没什么差别,看个人习惯吧。若要了解mysql存储过程,见:mysql存储过程。通过execute方法,也能方便的直接调用sql,就不多说了。

五、HibernateTemplate 和Session

    使用HibernateTemplate,有一个很重要的原因就在于我们不想直接控制事务,不想直接去获取,打开Session,开始一个事务,处理异常,提交一个事务,最后关闭一个Session,HibernateTemplate是Hibernate操作进行封装,我们只要简单的调用HibernateTemplate对象,传入hql和参数,就获得查询接口,至于事务的开启,关闭,都交给HibernateTemplate 对象来处理我们自己只专注于业务,不想去作这些重复而繁琐的操作。我们把这些责任全部委托给了 HibernateTemplate,然后使用声明式的配置来实现这样的功能。
    (在 Spring+Hibernate的集成环境里,如果DAO直接使用HibernateDaoSupport的getSession()方法获取 session进行数据操作而没有显式地关闭该session,那么程序表现为:每个session会打开一个connection,并且 connection会一直保持(因为没有显式地close).如果程序使用了c3p0连接池,则因为c3p0连接池默认最大连接数是15,程序会表现为当打开第15个连接时,程序处于停滞状态,等待从连接池获取新的连接. 在同样条件下,使用HibernateTemplate进行数据操作,就没有连接数持续增长的情况,程序结束时连接数归零.这印证了spring文档上所说:HibernateTemplate会对session进行了管理,能够确保Session实例的正确打开和关闭。需要注意的是:在Spring环境里,即使我们使用Hibernate原生的API,比如这里所说的使用HibernateDaoSupport的getSession()方法得到Session进行数据操作(而不是使用Spring自己提供的API,比如HibernateTemplate),这些操作也依然会被纳入spring管理的事务中去.原因是通过getSession()方法得到Session是一个绑定到当前事务上的session.此处可参考:http://www.javaeye.com/topic/110801.这就是为什么Spring文档中提到的:You can implement DAOs based on the plain Hibernate 3 API, while still being able to participate in Spring-managed transactions。即:HibernateDaoSupport的getSession()得到的Session会参与Spring管理的事务中,但是不能自动的关闭. HibernateTemplate 除能参与到 Spring管理的事务中,还 能够确保Session实例的正确打开和关闭

    (在myeclipse10.6中使用生成springDAO时,如果是hibernate4会直接生成 getCurrentSession().save(transientInstance)因为,hibernate4之后spring就没提供hibernateTemplate了;没有使用事务啊,没有关闭Session的操作。生成继承于BaseHibernateDAO的DAO是getSession().save(transientInstance);基本上没什么区别啊。 spring in action中说既然有了session就不需要再使用 HibernateTemplate ,直接使用getsession()就好,可是为什么在网上查的都是用HibernateTemplate 比较好呢。 群里询问,是说有HibernateTemplate 的封装当然好一些。但是现在都没用hibernate了 在用springdatajpa 写个方法名就搞定了 hql sql 都没有 也没有什么getsession)