声明式事务管理

来源:互联网 发布:基本公共卫生考核软件 编辑:程序博客网 时间:2024/05/22 09:00


我们一下说的都是spring 整合hibernate的事物配置。
Spring2.0及以后的版本中声明式事务有两种风格的配置:使用基于XML文件和基于注解式的声明式事务配置方法。具体参见<Spring Framework 开发参考手册> 9.5 声明式事务管理。
Spring2.0及以后的版本中声明式事务的配置与之前的版本有相当大的不同。主要差异在于不再需要配置TransactionProxyFactoryBean了。
Spring2.0之前的旧版本风格的配置仍然是有效的;你可以简单地认为新的<tx:tags/>替你定义了TransactionProxyFactoryBean。
Spring2.0之前的旧版本风格的配置示例如下:

//applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

<beans default-dependency-check="none" default-autowire="no" default-lazy-init="false">

 <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
  <property name="jndiName">
   <value>jdbc/award</value>
  </property>
 </bean>

 <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
  <property name="dataSource">
   <ref bean="dataSource" />
  </property>

  <property name="hibernateProperties">
   <props>
    <prop key="hibernate.dialect">org.hibernate.dialect.DB2Dialect</prop>
    <prop key="hibernate.show_sql">true</prop>
   </props>
  </property>
  <property name="mappingDirectoryLocations">
     <list>
    <value>classpath*:gov/cnca/aproduct/bo</value>
   <!--
     <value>classpath*:persist/role</value>
     <value>classpath*:persist/activity</value>
     <value>classpath*:persist/extension</value>
     <value>classpath*:persist/user</value>
           -->
           </list>
      
  </property>

  <!--
  <property name="mappingResources">
   <list>
    <value>gov/cnca/aproduct/bo/User.hbm.xml</value>
   </list>
  </property>
     -->
 </bean>
 

 <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
  <property name="sessionFactory">
   <ref local="sessionFactory" />
  </property>
 </bean>

    <bean id="transactionProxy" abstract="true" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
      <property name="transactionManager">
         <ref bean="transactionManager"/>
      </property>
      <property name="transactionAttributes">
         <props>
          <prop key="init*">PROPAGATION_REQUIRED,-SysException</prop>
            <prop key="save*">PROPAGATION_REQUIRED,-SysException</prop>
                <prop key="add*">PROPAGATION_REQUIRED,-SysException</prop>
                <prop key="remove*">PROPAGATION_REQUIRED,-SysException</prop>
                <prop key="delete*">PROPAGATION_REQUIRED,-SysException</prop>
                <prop key="update*">PROPAGATION_REQUIRED,-SysException</prop>
                <prop key="create*">PROPAGATION_REQUIRED,-SysException</prop>
                <prop key="query*">PROPAGATION_REQUIRED,readOnly</prop>
                <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
                <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
                <prop key="load*">PROPAGATION_REQUIRED,readOnly</prop>
                <prop key="uploadFile">PROPAGATION_REQUIRED,-SysException</prop>
         </props>
      </property>
    </bean>

<!-- service -->
 <!-- login -->
 <bean id="iUserHandleBusiService" parent="transactionProxy">
  <property name="target">
   <bean class="gov.cnca.aproduct.service.impl.UserHandleBusiServiceImpl">
    <property name="userDAO" ref="iUserDAO" />
    <property name="userFriendsDAO" ref="iUserFriendsDAO" />
    <property name="userIntegralDAO" ref="iUserIntegralDAO" />
    <property name="userAwardDAO" ref="iUserAwardDAO" />
    <property name="luckRateDAO" ref="iLuckRateDAO" />
    <property name="rateDAO" ref="iRateDAO" />
   </bean>
  </property>
 </bean>

 <bean id="iSendEmailForPasswordService" parent="transactionProxy">
  <property name="target">
   <bean class="gov.cnca.aproduct.service.impl.SendEmailForPasswordServiceImpl"></bean>
  </property>
 </bean>

 <bean id="iManageMemberService" parent="transactionProxy">
  <property name="target">
   <bean class="gov.cnca.aproduct.service.impl.ManageMemberServiceImpl">
    <property name="userDAO" ref="iUserDAO" />
    <property name="memberDAO" ref="iMemberDAO" />
    <property name="userIntegralDAO" ref="iUserIntegralDAO" />
    <property name="userAwardDAO" ref="iUserAwardDAO" />
   </bean>
  </property>
 </bean>
<!-- dao -->
 <bean id="iLuckRateDAO" name="iLuckRateDAO" class="gov.cnca.aproduct.dao.impl.LuckRateDAOImpl">
     <property name="sessionFactory" ref="sessionFactory"></property>
    </bean>
 <bean id="iRateDAO" name="iRateDAO" class="gov.cnca.aproduct.dao.impl.RateDAOImpl">
     <property name="sessionFactory" ref="sessionFactory"></property>
    </bean>
    <bean id="iUserDAO" name="iUserDAO" class="gov.cnca.aproduct.dao.impl.UserDAOImpl">
     <property name="sessionFactory" ref="sessionFactory"></property>
    </bean>
 <bean id="iUserFriendsDAO" name="iUserFriendsDAO" class="gov.cnca.aproduct.dao.impl.UserFriendsDAOImpl">
     <property name="sessionFactory" ref="sessionFactory"></property>
    </bean>
 <bean id="iUserIntegralDAO" name="iUserIntegralDAO" class="gov.cnca.aproduct.dao.impl.UserIntegralDAOImpl">
     <property name="sessionFactory" ref="sessionFactory"></property>
    </bean>
 <bean id="iUserAwardDAO" name="iUserAwardDAO" class="gov.cnca.aproduct.dao.impl.UserAwardDAOImpl">
     <property name="sessionFactory" ref="sessionFactory"></property>
    </bean>
 <bean id="iMemberDAO" name="iMemberDAO" class="gov.cnca.aproduct.dao.impl.MemberDAOImpl">
     <property name="sessionFactory" ref="sessionFactory"></property>
    </bean>

 对于特定的方法或方法命名模式,代理的具体事务行为由事务属性驱动,如下面的例子所示:
<prop key="insert*">
 ROPAGATION_REQUIRED, ISOLATION_READ_COMMITTED
</prop>
key属性确定代理应该给哪个方法增加事务行为。这样的属性最重要的部份是传播行为。有以下选项可供使用:
PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
PROPAGATION_SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER--以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED--如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。
前六个策略类似于EJB CMT,第七个(PROPAGATION_NESTED)是Spring所提供的一个特殊变量。
它要求事务管理器或者使用JDBC 3.0 Savepoint API提供嵌套事务行为(如Spring的DataSourceTransactionManager)。
事务属性中的readOnly标志表示对应的事务应该被最优化为只读事务。这是一个最优化提示。在一些情况下,一些事务策略能够起到显著的最优化效果,
例如在使用Object/Relational映射工具(如:Hibernate或TopLink)时避免dirty checking(试图“刷新”)。
在事务属性中还有定义“timeout”值的选项,指定事务超时为几秒。在JTA中,这将被简单地传递到J2EE服务器的事务协调程序,并据此得到相应的解释。
------------------------------------------------------------------
 Spring的事务管理是通过AOP代理实现的。其中的事务通知由元数据(目前基于XML或注解)驱动。代理对象与事务元数据结合产生了一个AOP代理,
它使用一个PlatformTransactionManager的实现配合TransactionInterceptor,在方法调用前后实施事务。spring使用aop机制管理jdbc的连接和事务。
它使用TransactionInterceptor类,Spring事务支持中的核心接口是org.springframework.transaction.PlatformTransactionManager。
为了实际执行事务,Spring所有的事务划分功能都通过传递适当的TransactionDefinition实例,委托给 PlatformTransactionManager。
尽管PlatformTransactionManager接口可以直接使用,应用程序通常配置具体的事务管理器并使用声明性事务来划分事务。Spring具有多种
PlatformTransactionManager实现,它们分为两类:
 局部事务策略即针对单个资源执行事务(主要是针对单个的数据库)。实现有 org.springframework.jdbc.datasource.DataSourceTransactionManager。
它用于jdbc数据源的配置,调用TransactionInterceptor开启一个事务,从DataSource得到一个connection并确保auto-commit设为false。
他用JdbcTemplate在一个线程内绑定一个JDBC connection,TransactionInterceptor负责提交事务,DataSourceTransactionManager
调用Connection.commit()关闭connection,并解除绑定(potentially allowing for one thread connection per data source)。
例如:
<beans>
 <bean id="DataSource" class="org.apache.commons.dbcp.BasicDataSource">
  <property name="driverClassName">
   <value>oracle.jdbc.driver.OracleDriver</value>
  </property>
  <property name="url">
   <value>jdbc:oracle:thin:@localhost:1521:hua2</value>
  </property>
  <property name="username">
   <value>user</value></property>
  <property name="password">
   <value>gotpassword</value>
  </property>
 </bean>
 
 <bean id="DataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="DataSource" />
 </bean>
 
 <bean id="tatanTransactionScriptsProxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
  <property name="beanNames">
   <list>
    <idref bean="tatanTransactionScripts" />
   </list>
  </property>
  <property name="interceptorNames">
   <list>
    <idref bean="DataSourceTransactionInterceptor" />
   </list>
  </property>
 </bean>
 
 <bean id="DataSourceTransactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
  <property name="transactionManager" ref="DataSourceTransactionManager" />
  <property name="transactionAttributeSource">
   <value>com.tatan.tatanTransactionScriptsImpl.*=PROPAGATION_REQUIRED</value>
  </property>
 </bean>
</beans>

transactionAttributesSource 属性指定每个方法的transaction attribute,PROPAGATION_REQUIRED说明在一个事务内这个方法被执行。
和EJB一样,默认的情况下,spring只有当unchecked exception被抛出时,才rollback事务,也可以自己加入checked exception。
tatanTransactionScripts被TransactionInterceptor封装,在一个事物内执行类的每一个方法。

   全局事务管理即执行有可能跨越多个资源的全局事务。主要对应的Spring类是org.springframework.transaction.jta.JtaTransactionManager,它委托给遵循JTA规范的J2EE服务器,也有例外。
spring支持JTA,只需要一个标准的JtaTransactionManager定义,数据库必须支持XA protocol,或者J2EE服务器提供支持XA规范的DataSource。
默认的Spring JtaTransactionManager设置将从标准的JNDI位置获取JTA的 javax.transaction.UserTransaction对象,该JNDI位置由J2EE指定:java: comp/UserTransaction。对于大多数标准J2EE环境下的用例来说,它工作良好。
但是,默认的 JtaTransactionManager不能执行事务挂起操作(即它不支持PROPAGATION_REQUIRES_NEW和 PROPAGATION_NOT_SUPPORTED)。原因是标准的JTA UserTransaction接口不支持挂起或恢复事务的操作;它只支持开始和完成新事务的操作。
为执行事务挂起操作,还需要提供javax.transaction.TransactionManager实例,按照JTA的规定,它提供标准的挂起和恢复方法。遗憾的是,J2EE没有为JTA TransactionManager定义标准的JNDI位置!
因此,必须使用特定于供应商的(vendor-specific)查寻机制。J2EE没有考虑把JTA TransactionManager接口作为它的公开API的一部分。JTA规范规定的TransactionManager接口原本是打算用于容器集成的。
但是为JTA TransactionManager定义标准的JNDI位置还是有重大意义的,尤其是对于轻量级容器(如Spring);然后,便可以以同样的方式来定位任意的J2EE服务器的JTA TransactionManager。
结合jboss JTA的Spring事务划分
oracle-ds.xml
<?xml version="1.0" encoding="UTF-8"?>

<datasources>
<xa-datasource>
  <jndi-name>XASpringDS</jndi-name>
  <track-connection-by-tx/>
  <isSameRM-override-value>false</isSameRM-override-value>
  <xa-datasource-class>oracle.jdbc.xa.client.OracleXADataSource</xa-datasource-class>
  <xa-datasource-property name="URL">jdbc:oracle:oci8:@orcl</xa-datasource-property>
  <xa-datasource-property name="User">SCOTT</xa-datasource-property>
  <xa-datasource-property name="Password">tiger</xa-datasource-property>
  <exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.OracleExceptionSorter</exception-sorter-class-name>
  <no-tx-separate-pools/>
</xa-datasource>
<mbean
  code="org.jboss.resource.adapter.jdbc.xa.oracle.OracleXAExceptionFormatter"
  name="jboss.jca:service=OracleXAExceptionFormatter">
  <depends optional-attribute-name="TransactionManagerService">
   jboss:service=TransactionManager</depends>
</mbean>
</datasources>
spring配置
<!-- Data source bean -->

<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
  <property name="jndiName"><value>java:/XASpringDS</value></property>
</bean>

<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>

Spring有效地将DAO实现从实际的运行时环境中分离出来,允许在J2EE容器之外轻松地测试或重用用。
Spring提供了多种事务策略,比如JtaTransactionManager和JDBC DataSourceTransactionManager,
前者委托给J2EE服务器的事务协调程序,后者则针对单个JDBC DataSource(即单个的目标数据库)执行事务。
通过对后端配置进行简单的更改,就能够轻松地调整事务策略适应另一个环境。

--------------------------------------------------------------

Spring事务抽象的关键是事务策略的概念。这个概念由org.springframework.transaction.PlatformTransactionManager接口定义如下:

public interface PlatformTransactionManager {

    TransactionStatus getTransaction(TransactionDefinition definition)
        throws TransactionException;

    void commit(TransactionStatus status) throws TransactionException;

    void rollback(TransactionStatus status) throws TransactionException;
}
这首先是一个SPI接口,虽然它也可以在 编程 中使用。注意按照Spring框架的哲学,PlatformTransactionManager 是一个 接口。
因而如果需要它可以很容易地被模拟和桩化。它也没有和一个查找策略如JNDI捆绑在一起:PlatformTransactionManager 的实现定义
和其他Spring IoC容器中的对象一样。这个好处使得即使使用JTA,也是一个很有价值的抽象:事务代码可以比直接使用JTA更加容易测试。
继续Spring哲学,可由任何 PlatformTransactionManager 的接口方法抛出的 TransactionException 是unchecked exception
(继承自java.lang.RuntimeException)的。底层的事务失败几乎总是致命的。很少情况下应用程序代码可以从它们中恢复,不过应用开发
者依然可以捕获并处理TransactionException,他们可以自由决定怎么干。getTransaction(..)方法根据一个类型为 TransactionDefinition
的参数返回一个 TransactionStatus 对象。返回的 TransactionStatus 对象可能代表一个新的或已经存在的事务(如果在当前调用堆栈有一个
符合条件的事务。如同J2EE事务环境,一个 TransactionStatus 也是和执行 线程 绑定的)。
TransactionDefinition接口指定:
事务隔离:当前事务和其它事务的隔离的程度。例如,这个事务能否看到其他事务未提交的写数据?
事务传播:通常在一个事务中执行的所有代码都会在这个事务中运行。但是,如果一个事务上下文已经存在,有几个选项可以指定一个事务性方法的执行行为:
 例如,简单地在现有的事务中继续运行(大多数情况);或者挂起现有事务,创建一个新的事务。Spring提供EJB CMT中常见的事务传播选项。
事务超时: 事务在超时前能运行多久(自动被底层的事务基础设施回滚)。
只读状态: 只读事务不修改任何数据。只读事务在某些情况下(例如当使用Hibernate时),是一种非常有用的优化。

这些设置反映了标准概念。如果需要,请查阅讨论事务隔离层次和其他核心事务概念的资源:理解这些概念在使用Spring框架和其他事务管理解决方案时是非常关键的。

TransactionStatus 接口为处理事务的代码提供一个简单的控制事务执行和查询事务状态的方法。这个概念应该是熟悉的,因为它们在所有的事务API中是相同的:

public interface TransactionStatus {

    boolean isNewTransaction();

    void setRollbackOnly();

    boolean isRollbackOnly();
}
使用Spring时,无论你选择编程式还是声明式的事务管理,定义一个正确的 PlatformTransactionManager 实现都是至关重要的。按照Spring的良好风格,
这种重要定义都是通过IoC实现的。一般来说,选择PlatformTransactionManager实现时需要知道当前的工作环境,如(1)JDBC、(2)JTA、(3)Hibernate等。

(1)下面的例子来自Spring示例应用——jPetStore——中的dataAccessContext-local.xml文件,其中展示了一个局部PlatformTransactionManager实现
是怎么定义的(仅限于纯粹JDBC环境)我们必须先定义一个JDBC DataSource,然后使用Spring的DataSourceTransactionManager,并传入指向DataSource的引用。

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
  <property name="driverClassName" value="${jdbc.driverClassName}" />
  <property name="url" value="${jdbc.url}" />
  <property name="username" value="${jdbc.username}" />
  <property name="password" value="${jdbc.password}" />
</bean>
PlatformTransactionManager bean的定义如下:

<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource"/>
</bean>
在dao中如下示例:
public class BigAmountRuleHibernateDAO extends BaseDaoHibernate implements BigAmountRuleDAO{

 private JdbcTemplate jdbcTemplate;
 
  /**
   * jdbcTemplate根查询列表数据
   */
 public List findListByJdbcTemplate(String sql,String[] args){
  List list = null;
  if(args != null)
   list=jdbcTemplate.queryForList(sql, args);
  else
   list=jdbcTemplate.queryForList(sql);
  return list;
 }
 
 public JdbcTemplate getJdbcTemplate() {
  return jdbcTemplate;
 }
 
 public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
  this.jdbcTemplate = jdbcTemplate;
 }
}
(2)如果我们在J2EE容器里使用JTA,就像示例中 'dataAccessContext-jta.xml' 文件所示,我们将通过JNDI和Spring的 JtaTransactionManager 来获取一个容
器管理的 DataSource。JtaTransactionManager 不需要知道 DataSource 和其他特定的资源,因为它将使用容器提供的全局事务管理。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:jee="http://www.springframework.org/schema/jee"
    xsi:schemaLocation="
         http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
         http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.0.xsd">

  <jee:jndi-lookup id="dataSource" jndi-name="jdbc/jpetstore"/>

  <bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager" />

  <!-- other <bean/> definitions here -->

</beans>
注意
上面 'dataSource' 的bean定义使用了 'jee' 名称空间下的 <jndi-lookup/> 标签。想了解更多的配置信息, 请看附录 A, XML Schema-based configuration,
关于 <jee/> 标签的信息,可参考 第 A.2.3 节 “The jee schema” 节。

(3)我们也可以很容易地使用Hibernate局部事务,就像下面的Spring框架的 PetClinic 示例应用中的例子一样)。这种情况下,我们需要定义一个Hibernate的
LocalSessionFactoryBean,应用程序从中获取到Hibernate Session 实例。DataSource 的bean定义同上例类似(这里不再展示)。不过,
如果是一个JEE容器提供的 DataSource,它将由JEE容器自身,而不是Spring框架来管理事务。这种情况中'txManager' bean的类型为 HibernateTransactionManager。
同样地,DataSourceTransactionManager 需要一个指向 DataSource 的引用,而 HibernateTransactionManager 需要一个指向 SessionFactory 的引用。

<bean id="sessionFactory" class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
  <property name="dataSource" ref="dataSource" />
  <property name="mappingResources">
    <list>
      <value>org/springframework/samples/petclinic/hibernate/petclinic.hbm.xml</value>
    </list>
  </property>
  <property name="hibernateProperties">
    <value>
   hibernate.dialect=${hibernate.dialect}
 </value>
  </property>
</bean>

<bean id="txManager" class="org.springframework.orm.hibernate.HibernateTransactionManager">
  <property name="sessionFactory" ref="sessionFactory" />
</bean>

使用getHibernateTemplate()示例:
public class BigAmountRuleHibernate extends BaseDaoHibernate implements BigAmountRuleDAO{

 public void saveOrUpdateEntity(Object obj){
  this.getHibernateTemplate().save(obj);
 }
 
}
我们可以简单地使用 JtaTransactionManager 来处理Hibernate事务和JTA事务,就像我们处理JDBC,或者任何其它的资源策略一样。

<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>
注意任何资源的JTA配置都是这样的,因为它们都是全局事务,可以支持任何事务性资源。

在所有这些情况下,应用程序代码根本不需要做任何改动。我们仅仅通过改变配置就可以改变事务管理方式,即使这些更改是在局部事务和全局事务间切换。

--------------------------------------------------------------

先提出一个问题:spring是否能mixed JDBC和ORM

下面以具体示例来说明事物执行的来龙去脉。
配置:jdk6.o + spring2.5.6 + hibernate2
先看一段代码:
 Connection conn = Conn.getConnection();
    conn.setAutoCommit(false);
    ……..
    ……...
    conn.rollback();
    conn.commit();
因此我们要明确一个事实:数据库的事务是针对 Connection 的。
接下来是具体的配置。
// applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:aop="http://www.springframework.org/schema/aop"
 xmlns:jee="http://www.springframework.org/schema/jee"
 xmlns:tx="http://www.springframework.org/schema/tx"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
           http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.0.xsd">
 <!--
  <jee:jndi-lookup id="dataSource" jndi-name="jdbc/MIBS48"/>
 -->

 <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
  <property name="location" value="/WEB-INF/jdbc.properties" />
  <!--
   <property name="locations"> <list>
   <value>/WEB-INF/jdbc.properties</value>
   <value>/WEB-INF/hiawardwebservice.properties</value> </list>
   </property>
  -->
 </bean>
 
 <!-- 支持 @Transactional 标记 --> 
 <tx:annotation-driven proxy-target-class="true" transaction-manager="txManager"/>
 
 <bean id="defaultLobHandler" class="org.springframework.jdbc.support.lob.DefaultLobHandler" lazy-init="false" />
 
 <!-- 配置数据源
  <bean id="dataSource"
   class="com.mchange.v2.c3p0.ComboPooledDataSource"
   destroy-method="close">
   <property name="driverClass">
    <value>com.ibm.db2.jcc.DB2Driver</value>
   </property>
   <property name="jdbcUrl">
    <value>jdbc:db2://197.0.3.44:60004/MIBSSE</value>
   </property>
   <property name="user">
    <value>mibs</value>
   </property>
   <property name="password">
    <value>mibsyx</value>
   </property>
   <property name="initialPoolSize">
    <value>5</value>
   </property>
   <property name="minPoolSize">
    <value>5</value>
   </property>
   <property name="maxPoolSize">
    <value>32</value>
   </property>
   <property name="checkoutTimeout">
    <value>0</value>
   </property>
   <property name="maxIdleTime">
    <value>0</value>
   </property>
   <property name="maxStatements">
    <value>0</value>
   </property>
   <property name="maxStatementsPerConnection">
    <value>100</value>
   </property>  
  </bean>
  -->
 <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
  <property name="driverClassName" value="${jdbc.driverClassName}" />
  <property name="url" value="${jdbc.url}" />
  <property name="username" value="${jdbc.username}" />
  <property name="password" value="${jdbc.password}" />
 </bean>
 
 <!--org.springframework.jdbc.datasource.DataSourceTransactionManager 或 org.springframework.orm.hibernate3.HibernateTransactionManager -->
 <bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
  <!-- <property name="dataSource" ref="dataSource" /> -- > <!-- 用DataSourceTransactionManager则需要此属性,而HibernateTransactionManager不需要。 -->
  <property name="sessionFactory">  <!-- 如果用DataSourceTransactionManager则去掉 -->
   <ref local="sessionFactory" />
  </property>
  
 </bean>
 
 <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
  <property name="dataSource">
   <ref bean="dataSource" />
  </property>
 </bean>
 <bean id="dataSourceJbpm" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
  <property name="driverClassName" value="${jdbc.driverClassName}" />
  <property name="url" value="${jdbc.url}" />
  <property name="username" value="${jdbc.jbpmuser}" />
  <property name="password" value="${jdbc.password}" />
 </bean>
 <bean id="sessionFactoryJbpm" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
  <property name="dataSource">
   <ref bean="dataSource" />
  </property>
  <property name="mappingJarLocations">
   <list>
   <value>WEB-INF/lib/jbpm-jpdl.jar</value>
   <value>WEB-INF/lib/jbpm-identity.jar</value>
   </list>
  </property>
  <property name="hibernateProperties">
   <props>
    <prop key="hibernate.show_sql">true</prop>
    <prop key="hibernate.format_sql">true</prop>
    <prop key="hibernate.dialect">
     org.hibernate.dialect.DB2Dialect
    </prop>
    <prop key="hibernate.cache.use_query_cache">true</prop>
    <!--
     <prop key="hibernate.cache.provider_class">
     org.hibernate.cache.EhCacheProvider </prop>
    -->
    <prop key="hibernate.cache.use_second_level_cache">
     true
    </prop>
    <prop key="hibernate.cache.provider_class">
     org.hibernate.cache.EhCacheProvider
    </prop>
    <prop key="hibernate.jdbc.batch_size">25</prop>
   </props>
  </property>
 </bean>
 <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
  <property name="dataSource" ref="dataSource" />
  
  <property name="mappingDirectoryLocations">
   <list>
    <value>
     classpath:/com/hiaward/xbankmibs/cpc/dao/model
    </value>
    <value>
     classpath:/com/hiaward/xbankmibs/dao/journal/model
    </value>
    <value>
     classpath:/com/hiaward/xbankmibs/dao/taskmanager/model
    </value>
    <value>
     classpath:/com/hiaward/xbankmibs/dao/service/model
    </value>
    <value>
     classpath:/com/hiaward/xbankmibs/dao/security/model
    </value>
    <value>
     classpath:/com/hiaward/xbankmibs/message/model
    </value>
    <!-- 日常管理 映射文件 liangxuewei 2009-02-19 -->
    <value>
     classpath:/com/hiaward/xbankmibs/dao/daily/model
    </value>
          <value>
            classpath:/com/hiaward/xbankmibs/dao/accountIgt/model
          </value>
    <value>
     classpath:/com/hiaward/xbankmibs/dao/cardmade/model
    </value>
    <value>
            classpath:/com/hiaward/xbankmibs/dao/scenarioservice/model
          </value>
          <!-- 支付结算清差处理 -->
          <value>
            classpath:/com/hiaward/xbankmibs/dao/pay/model
          </value>
   </list>
  </property>
  <property name="hibernateProperties">
   <props>
    <prop key="hibernate.show_sql">true</prop>
    <prop key="hibernate.format_sql">true</prop>
    <prop key="hibernate.dialect">
     org.hibernate.dialect.DB2Dialect
    </prop>
    <prop key="hibernate.cache.use_query_cache">true</prop>
    <!--
     <prop key="hibernate.cache.provider_class">
     org.hibernate.cache.EhCacheProvider </prop>
    -->
    <prop key="hibernate.cache.use_second_level_cache">
     true
    </prop>
    <prop key="hibernate.cache.provider_class">
     org.hibernate.cache.EhCacheProvider
    </prop>
    <prop key="hibernate.jdbc.batch_size">25</prop>
   </props>
  </property>
 </bean>
 <!-- lob专用sessionFactory  -->
 <bean id="sessionFactoryForLob"
  class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
  <property name="dataSource" ref="dataSource" />
  <property name="lobHandler" ref="defaultLobHandler"/>
  <property name="mappingDirectoryLocations">
   <list>
    <value>classpath:/com/hiaward/xbankmibs/dao/service/model
    </value>
    <value>classpath:/com/hiaward/xbankmibs/dao/security/model
    </value>
    <value>classpath:/com/hiaward/xbankmibs/message/model
    </value>
    <value>
     classpath:/com/hiaward/xbankmibs/dao/journal/model
    </value>
   </list>
  </property>
  <property name="hibernateProperties">
   <props>
    <prop key="hibernate.show_sql">true</prop>
    <prop key="hibernate.format_sql">true</prop>
    <prop key="hibernate.dialect">
     org.hibernate.dialect.DB2Dialect
    </prop>
      <prop key="hibernate.cache.use_query_cache">true</prop>
     <prop key="hibernate.cache.provider_class">
     org.hibernate.cache.EhCacheProvider </prop>
    <prop key="hibernate.cache.use_second_level_cache">
     false
    </prop>
    <prop key="hibernate.cache.provider_class">
     org.hibernate.cache.EhCacheProvider
    </prop>
    <prop key="hibernate.jdbc.batch_size">0</prop> 
   </props>
  </property>
 </bean>
</beans>

// Service:BigAmountRuleServiceImpl.java

@SuppressWarnings("unchecked")
@Transactional
public class BigAmountRuleServiceImpl implements BigAmountRuleService{
 protected final Log log = LogFactory.getLog(getClass());
 private BigAmountRuleDAO bigAmountRuleDAO;
 
 /**
  * 查询条数
  * @param scenarioCode
  * @param orgCode
  * @param accountType
  * @param currencyCode
  * @return
  */
 @Transactional (readOnly=false, isolation = Isolation.READ_COMMITTED) 
 public int getTransRegisterCount(String scenarioCode,String orgCode,String accountType,String currencyCode){
  List<String[]> objTempList = null;
  if(scenarioCode != null || orgCode != null || accountType != null || currencyCode != null){
   String[] scenarioCodeArr = scenarioCode != null?scenarioCode.split(","):null;
   String[] orgCodeArr = orgCode != null?orgCode.split(","):null;
   String[] accountArr = accountType!=null?accountType.split(","):null;
   String[] currencyArr = currencyCode!=null?currencyCode.split(","):null;
   
   objTempList = new ArrayList<String[]>();
   
   objTempList.add(0,scenarioCodeArr);
   objTempList.add(1,orgCodeArr);
   objTempList.add(2,accountArr);
   objTempList.add(3,currencyArr);
  }
    
  int iCount = bigAmountRuleDAO.getTotalCount(objTempList);  // 使用了jdbcTemplate
  return iCount;
 }
 /**
  *
  * @param scenarioCode
  * @param orgCode
  * @param accountType
  * @param currencyCode
  * @return
  */
 @Transactional (readOnly=false, isolation = Isolation.READ_COMMITTED) 
 public Map<String,Object> getQueryDataList(String scenarioCode,String orgCode,String accountType,String currencyCode,int pStart,int size){
  List<String[]> objTempList = null;
  if(scenarioCode != null || orgCode != null || accountType != null || currencyCode != null){
   String[] scenarioCodeArr = scenarioCode != null?scenarioCode.split(","):null;
   String[] orgCodeArr = orgCode != null?orgCode.split(","):null;
   String[] accountArr = accountType!=null?accountType.split(","):null;
   String[] currencyArr = currencyCode!=null?currencyCode.split(","):null;
   objTempList = new ArrayList<String[]>();
   
   objTempList.add(0,scenarioCodeArr);
   objTempList.add(1,orgCodeArr);
   objTempList.add(2,accountArr);
   objTempList.add(3,currencyArr);

  }
    
  Map<String,Object> map = bigAmountRuleDAO.queryByList(objTempList, pStart, size); /*使用了getHibernateTemplate().方法名()和 回调
                       return this.getHibernateTemplate().executeFind(new HibernateCallback() {
                        public Object doInHibernate(Session session) throws SQLException,
                          HibernateException {
                         List vehicleList = null;
                         Query q = session.createQuery(sql);
                         q.setFirstResult(currentPage);
                         q.setMaxResults(pageSize);
                         vehicleList = q.list();
                         return vehicleList;
                         }
                       });
                      */
  return map;
 }
 
 @Transactional (readOnly=false, isolation = Isolation.READ_COMMITTED) 
 public int getRecordCountBySql(String sql){
  return bigAmountRuleDAO.getListCount(sql);
 }
 
 
 public BigAmountRuleDAO getBigAmountRuleDAO() {
  return bigAmountRuleDAO;
 }

 public void setBigAmountRuleDAO(BigAmountRuleDAO bigAmountRuleDAO) {
  this.bigAmountRuleDAO = bigAmountRuleDAO;
 }
}

//DAO:BigAmountRuleHibernate.java

@SuppressWarnings("unchecked")
public class BigAmountRuleHibernate extends BaseDaoHibernate implements BigAmountRuleDAO{

 private JdbcTemplate jdbcTemplate;
 
 public JdbcTemplate getJdbcTemplate() {
  return jdbcTemplate;
 }
 
 public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
  this.jdbcTemplate = jdbcTemplate;
 }
 
 public void saveOrUpdateEntity(Object obj){
  this.getHibernateTemplate().save(obj);
 }
 
 public void deleteEntity(Object obj){
  this.getHibernateTemplate().delete(obj);
 }
  /**
   * jdbcTemplate根查询列表数据
   */
 public List findListByJdbcTemplate(String sql,String[] args){
  List list = null;
  if(args != null)
   list=jdbcTemplate.queryForList(sql, args);
  else
   list=jdbcTemplate.queryForList(sql);
  return list;
 }
 
 public List findList(String sql,String[] args){
  return jdbcTemplate.queryForList(sql, args);
 }
 
 public Object getEntityByCondition(){
  return null;
 }
 /**
  * 新增和修改映射和大额核实规则信息
  */
 public String saveByList(List<String> list)throws Exception{
  int iRef = 0;
  int iAchk = 0;
  
  Session session = this.getHibernateTemplate().getSessionFactory().openSession();
  try{
  Transaction tx = session.beginTransaction();
  
  for(int i=0;i<list.size();i++){
   String strArr = list.get(i);
   String[] arr = strArr.split(",");
   
   //新增前查看当前币种的规则是否已经存在
   String blnExistRuleSql = "select ref.rscerefcode from Rsceref ref,Amountcheck chk where ref.ruletype='11' and ref.rulecode = chk.bmcrcode and chk.monetarycategory=? and ref.scenariocode=? and ref.orgcode=?";
   List<Long> listExistRuleSql = this.getHibernateTemplate().find(blnExistRuleSql,new Object[]{arr[3],arr[0],arr[1]});
   //新增到规则
   Amountcheck chkObj = new Amountcheck();
   chkObj.setAccountcategory(arr[2]);
   chkObj.setMonetarycategory(arr[3]);
   chkObj.setMonetary(Double.valueOf(arr[4]));
   chkObj.setOperator(arr[5]);
   chkObj.setUpdatedatetime(new Date());
   this.getHibernateTemplate().save(chkObj);
   iAchk++;
   //新增映射
   if(listExistRuleSql != null && listExistRuleSql.size() > 0){//更新映射引用的规则ID
    String updateSql = "update Rsceref ref set ref.rulecode = ? where ref.rscerefcode = ?";
    getHibernateTemplate().bulkUpdate(updateSql, new Object[]{chkObj.getBmcrcode(),listExistRuleSql.get(0)});    
   }
   else{//当前币种对应的规则映射表中没有 直接新增
    Rsceref refObj = new Rsceref();
    refObj.setScenariocode(arr[0]);
    refObj.setOrgcode(arr[1]);
    refObj.setRulecode(chkObj.getBmcrcode());
    refObj.setRuletype("11");
    this.getHibernateTemplate().save(refObj);
    iRef++;
   }
  }
  tx.commit();
  }catch(Exception e){
   throw new Exception(e);
  }finally{
   if(session != null)
    session.close();
   
  }
  return iRef+","+iAchk;
 }
 /**
  * 批量删除
  */
 public int deleteByList(List<String> list){
  StringBuffer sql = new StringBuffer();
  sql.append("from Rsceref ref where");
  sql.append(" ruletype = '11' and (");
  for(int i=0;i<list.size();i++){
   String strArr = list.get(i);
   String[] arr = strArr.split(",");
   //删除映射表
   
   sql.append("(ref.scenariocode='").append(arr[0]);
   sql.append("' and ref.orgcode='").append(arr[1]).append("' and ref.rulecode in");
   sql.append("(select chk.bmcrcode from Amountcheck chk where chk.accountcategory='").append(arr[2]);
   sql.append("' and chk.monetarycategory='").append(arr[3]).append("')) or");
  }
  String str = sql.substring(0, sql.length()-2);
  str = str+")";
  int iCount = Integer.parseInt(this.getHibernateTemplate().find("select count(*) "+str).get(0).toString());
  this.getHibernateTemplate().bulkUpdate("delete "+str);
  return iCount;
 }
 /**
  * 根据多选删除
  * @param args
  */
 public void deleteBySelect(String[] args){
  StringBuffer strId = new StringBuffer();
  strId.append("delete from Rsceref where ruletype = '11' and rscerefcode in");
  for(int i=0;i<args.length;i++){
   if(i==0){
    strId.append("(");
    strId.append(args[i]);
   }
   else{
    strId.append(",");
    strId.append(args[i]);
   }
  }
  strId.append(")");
  
  jdbcTemplate.execute(strId.toString());
 }
 
 /**
  * 查询记录
  */
 public Map<String,Object> queryByList(List<String[]> list,int pStart,int size){
  Map<String,Object> map = new HashMap<String,Object>();
  List listR = new ArrayList();
  StringBuffer sql=new StringBuffer();
  sql.append("from Rsceref ref,Amountcheck chk  where ref.rulecode = chk.bmcrcode and ref.ruletype='11'");
  
  if(list != null && list.size()>0){

   String[] scenarioCodeArr = list.get(0);
   String[] orgCodeArr = list.get(1);
   String[] accountArr = list.get(2);
   String[] currencyArr = list.get(3);
   
   if(scenarioCodeArr!=null && orgCodeArr != null && accountArr != null && currencyArr != null){
    sql.append(" and (");
    for(int i=0;i<scenarioCodeArr.length;i++){
     for(int j=0;j<orgCodeArr.length;j++){
      for(int k=0;k<accountArr.length;k++){
       for(int m=0;m<currencyArr.length;m++){
        sql.append("(ref.scenariocode='");
        sql.append(scenarioCodeArr[i]);
        sql.append("'");         

        sql.append(" and ref.orgcode='");
        sql.append(orgCodeArr[j]);
        sql.append("'");
        
        sql.append(" and chk.accountcategory='");
        sql.append(accountArr[k]);
        sql.append("'");
        
        sql.append(" and chk.monetarycategory='");
        sql.append(currencyArr[m]);
        sql.append("') or ");
       }
      }
     }
    }
    sql = new StringBuffer(sql.substring(0, sql.lastIndexOf("or")));
    sql.append(")");
   }
   else if(scenarioCodeArr!=null && orgCodeArr != null && accountArr != null){
    sql.append(" and (");
    
    for(int i=0;i<scenarioCodeArr.length;i++){
     for(int j=0;j<orgCodeArr.length;j++){
      for(int k=0;k<accountArr.length;k++){
       sql.append("(ref.scenariocode='");
       sql.append(scenarioCodeArr[i]);
       sql.append("'");         

       sql.append(" and ref.orgcode='");
       sql.append(orgCodeArr[j]);
       sql.append("'");
       
       sql.append(" and chk.accountcategory='");
       sql.append(accountArr[k]);
       sql.append("') or ");
      }
     }
    }
    
    sql = new StringBuffer(sql.substring(0, sql.lastIndexOf("or")));
    sql.append(")");
   }
  }

  sql.append(" order by ref.scenariocode,ref.orgcode,chk.accountcategory,chk.monetarycategory");

  listR = findPage(sql.toString(),pStart,size);

  List<Amountcheck> listEntity = new ArrayList();
  Map<String,String> mapName = new HashMap<String,String>();
  
  List listName = this.getHibernateTemplate().find("select strscenarionum,strscenarioname from Scenario");
  
  for(int i=0;i<listName.size();i++){
   Object[] obj = (Object[])listName.get(i);
   mapName.put(obj[0].toString(), obj[1].toString());
  }
  for(int i=0;i<listR.size();i++){
   Object[] obj = (Object[])listR.get(i);
   Rsceref refObj = (Rsceref)obj[0];
   Amountcheck chk = (Amountcheck)obj[1];
   
   chk.setRscerefcode(refObj.getRscerefcode());
   System.out.println(refObj.getScenariocode());
   chk.setScenarioName(mapName.get(refObj.getScenariocode()));
   chk.setScenarioCode(refObj.getScenariocode());
   chk.setOrgCode(refObj.getOrgcode());
   chk.setStrMonetary(NumberUtil.getNormalStyle(chk.getMonetary(), 2));
   listEntity.add(chk);
  }
  map.put("listEntity", listEntity);
  map.put("bigAmounChecktSql", sql.toString());
  return map;
 }
 /**
  * 分页方法
  */
 public List findPage(final String sql, final int currentPage,final int pageSize) {
  return this.getHibernateTemplate().executeFind(new HibernateCallback() {
   public Object doInHibernate(Session session) throws SQLException,
     HibernateException {
    List vehicleList = null;
    Query q = session.createQuery(sql);
    q.setFirstResult(currentPage);
    q.setMaxResults(pageSize);
    vehicleList = q.list();
    return vehicleList;
   }
  });
 } 
}


以action -> service -> dao 这种调用路径执行时,因为通过@Transactional我们把连接点(joinpoint)加入到service层,所以
在调用service的方法如getTransRegisterCount()还未进入时,就会调用TransactionInterceptor开启一个事务,进入
Cglib2AopProxy$CglibMethodInvocation(ReflectiveMethodInvocation).proceed(),如下
代码1.

 public Object proceed() throws Throwable {
  // We start with an index of -1 and increment early.
  //interceptorsAndDynamicMethodMatchers中包含[org.springframework.transaction.interceptor.TransactionInterceptor@1b53927]
  if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
   return invokeJoinpoint();
  }

  Object interceptorOrInterceptionAdvice =
      this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
  if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
   // Evaluate dynamic method matcher here: static part will already have
   // been evaluated and found to match.
   InterceptorAndDynamicMethodMatcher dm =
       (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
   if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
    return dm.interceptor.invoke(this);
   }
   else {
    // Dynamic matching failed.
    // Skip this interceptor and invoke the next in the chain.
    return proceed();
   }
  }//与切入点匹配,调用匹配成功的方法,因为我们是对所有service的public方法开启事物,以这里为例就是
  else {
   // It's an interceptor, so we just invoke it: The pointcut will have
   // been evaluated statically before this object was constructed.
   // 调用interceptor:TransactionInterceptor.java 的invoke() ,见代码2.
   return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
  }
 }
 
代码2.
// TransactionTnterceptor.java
 public Object invoke(final MethodInvocation invocation) throws Throwable {
  // Work out the target class: may be <code>null</code>.
  // The TransactionAttributeSource should be passed the target class
  // as well as the method, which may be from an interface.
  // 这里对应的是你的service:BigAmountRuleServiceImpl
  Class targetClass = (invocation.getThis() != null ? invocation.getThis().getClass() : null);

  // If the transaction attribute is null, the method is non-transactional.
  final TransactionAttribute txAttr =
    getTransactionAttributeSource().getTransactionAttribute(invocation.getMethod(), targetClass);
  final String joinpointIdentification = methodIdentification(invocation.getMethod());

  if (txAttr == null || !(getTransactionManager() instanceof CallbackPreferringPlatformTransactionManager)) {
   // Standard transaction demarcation with getTransaction and commit/rollback calls.
   // TransactionInfo 中拥有TransactionAttribute,TransactionStatus引用,TransactionStatus(DefaultTransactionStatus)中拥有Object transaction。
   // 创建事物,txAttr包含隔离级别、传播行为、是否只读。joinpointIdentification = com.hiaward.xbankmibs.cpc.service.impl.BigAmountRuleServiceImpl.getRecordCountBySql
   // 见代码3.
   TransactionInfo txInfo = createTransactionIfNecessary(txAttr, joinpointIdentification);
   Object retVal = null;
   try {
    // This is an around advice: Invoke the next interceptor in the chain.
    // This will normally result in a target object being invoked.
    /*
    * 执行代码1.的
    *
    * if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
    *  return invokeJoinpoint();
    * }
    * 执行代码代码15.
    *
    * 之后调用service的方法,这里是getRecordCountBySql(String sql),在执行DAO的方法。
    *  public int getListCount(String sql){
    *
    *  return this.getHibernateTemplate().find(sql).size();
    *
    *  }
    * 见代码10.
    */
    retVal = invocation.proceed();
   }
   catch (Throwable ex) {
    // target invocation exception
    completeTransactionAfterThrowing(txInfo, ex);
    throw ex;
   }
   finally {
    cleanupTransactionInfo(txInfo); // Reset the TransactionInfo ThreadLocal.
   }
   // 刷新、提交、关闭connection。见代码16. 这样整个流程跑完了。
   commitTransactionAfterReturning(txInfo);
   return retVal;
  }

  else {
   // It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
   try {
    Object result = ((CallbackPreferringPlatformTransactionManager) getTransactionManager()).execute(txAttr,
      new TransactionCallback() {
       public Object doInTransaction(TransactionStatus status) {
        TransactionInfo txInfo = prepareTransactionInfo(txAttr, joinpointIdentification, status);
        try {
         return invocation.proceed();
        }
        catch (Throwable ex) {
         if (txAttr.rollbackOn(ex)) {
          // A RuntimeException: will lead to a rollback.
          if (ex instanceof RuntimeException) {
           throw (RuntimeException) ex;
          }
          else {
           throw new ThrowableHolderException(ex);
          }
         }
         else {
          // A normal return value: will lead to a commit.
          return new ThrowableHolder(ex);
         }
        }
        finally {
         cleanupTransactionInfo(txInfo);
        }
       }
      });

    // Check result: It might indicate a Throwable to rethrow.
    if (result instanceof ThrowableHolder) {
     throw ((ThrowableHolder) result).getThrowable();
    }
    else {
     return result;
    }
   }
   catch (ThrowableHolderException ex) {
    throw ex.getCause();
   }
  }
 }

代码3. TransactionAspectSupport.java
 /**
  * Create a transaction if necessary based on the given TransactionAttribute.
  * <p>Allows callers to perform custom TransactionAttribute lookups through
  * the TransactionAttributeSource.
  * @param txAttr the TransactionAttribute (may be <code>null</code>)
  * @param joinpointIdentification the fully qualified method name
  * (used for monitoring and logging purposes)
  * @return a TransactionInfo object, whether or not a transaction was created.
  * The <code>hasTransaction()</code> method on TransactionInfo can be used to
  * tell if there was a transaction created.
  * @see #getTransactionAttributeSource()
  */
 protected TransactionInfo createTransactionIfNecessary(TransactionAttribute txAttr, final String joinpointIdentification) {

  // If no name specified, apply method identification as transaction name.
  // 如果事物没有指定名字,则把方法标识作为事物名。
  if (txAttr != null && txAttr.getName() == null) {
   txAttr = new DelegatingTransactionAttribute(txAttr) {
    public String getName() {
     return joinpointIdentification;
    }
   };
  }

  TransactionStatus status = null;
  if (txAttr != null) {
   // Return the transaction manager.这里是org.springframework.orm.hibernate3.HibernateTransactionManager
   PlatformTransactionManager tm = getTransactionManager();
   if (tm != null) {
    // 得到事物状态对象(newTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);))
    // 见代码4.
    status = tm.getTransaction(txAttr);
   }
   else {
    if (logger.isDebugEnabled()) {
     logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +
       "] because no transaction manager has been configured");
    }
   }
  }
  return prepareTransactionInfo(txAttr, joinpointIdentification, status);
 }

代码4.
 /**
  * This implementation handles propagation behavior. Delegates to
  * <code>doGetTransaction</code>, <code>isExistingTransaction</code>
  * and <code>doBegin</code>.
  * @see #doGetTransaction
  * @see #isExistingTransaction
  * @see #doBegin
  */
 public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
  // 返回DataSourceTransactionObject 对象txObject,并txObject.setConnectionHolder(conHolder, false),
  // conHolder可能是null(如果TransactionSynchronizationManager的ThreadLocal resources 中不存在这个ConnectionHolder)。
  // 见代码5.
  Object transaction = doGetTransaction();

  // Cache debug flag to avoid repeated checks.
  boolean debugEnabled = logger.isDebugEnabled();
  //interface TransactionAttribute extends TransactionDefinition
  if (definition == null) {
   // Use defaults if no transaction definition given.
   definition = new DefaultTransactionDefinition();
  }
  
  //transaction.getConnectionHolder() != null && transaction.getConnectionHolder().isTransactionActive()
  if (isExistingTransaction(transaction)) {
   // Existing transaction found -> check propagation behavior to find out how to behave.
   return handleExistingTransaction(definition, transaction, debugEnabled);
  }

  // Check definition settings for new transaction.
  if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
   throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
  }

  // No existing transaction found -> check propagation behavior to find out how to proceed.
  if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
   throw new IllegalTransactionStateException(
     "No existing transaction found for transaction marked with propagation 'mandatory'");
  }
  else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
     definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
           definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
          
   SuspendedResourcesHolder suspendedResources = suspend(null);
   if (debugEnabled) {
    logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
   }
   try {
    // 设置事物(DataSourceTransactionObject)对象txObject的属性,如isolation level(隔离级别),
    // 绑定ConnectionHolder和SessionHolder到TransactionSynchronizationManager的ThreadLocal resources 中。
    // 见代码6.事物开启。
    doBegin(transaction, definition);
   }
   catch (RuntimeException ex) {
    resume(null, suspendedResources);
    throw ex;
   }
   catch (Error err) {
    resume(null, suspendedResources);
    throw err;
   }
   boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
   
   // Create a new TransactionStatus(DefaultTransactionStatus ,其中包含transaction属性) for the given arguments ,
   // initializing transaction synchronization as appropriate(TransactionSynchronizationManager的ThreadLocal 变量).
   return newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, suspendedResources); 
  }
  else {
   // Create "empty" transaction: no actual transaction, but potentially synchronization.
   boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
   return newTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
  }
 }
 
代码5.
 /**
 * 返回一个事物对象HibernateTransactionObject,并对其sessionHolder、connectionHolder属性赋值。
 *
 */
 protected Object doGetTransaction() {
  HibernateTransactionObject txObject = new HibernateTransactionObject();
  txObject.setSavepointAllowed(isNestedTransactionAllowed());

     // 从(当前线程绑定的)变量resource中查找是否存在(用来装载Session的)SessionHolder,
     // 这个变量resource由TransactionSynchronizationManager来维护。如果存在则放到线程对象txObject中。
  SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(getSessionFactory());
  if (sessionHolder != null) {
   if (logger.isDebugEnabled()) {
    logger.debug("Found thread-bound Session [" + SessionFactoryUtils.toString(sessionHolder.getSession()) + "] for Hibernate transaction");
   }
   txObject.setSessionHolder(sessionHolder);
  }
  else if (this.hibernateManagedSession) {
   try {
    Session session = getSessionFactory().getCurrentSession();
    if (logger.isDebugEnabled()) {
     logger.debug("Found Hibernate-managed Session [" + SessionFactoryUtils.toString(session) + "] for Spring-managed transaction");
    }
    txObject.setExistingSession(session);
   }
   catch (HibernateException ex) {
    throw new DataAccessResourceFailureException( "Could not obtain Hibernate-managed Session for Spring-managed transaction", ex);
   }
  }

  if (getDataSource() != null) {
  
   // 从(当前线程绑定的)变量resource中查找是否存在(用来装载Connection的)ConnectionHolder,
      // 这个变量resource由TransactionSynchronizationManager来维护。如果存在则放到线程对象txObject中。
   ConnectionHolder conHolder = (ConnectionHolder)TransactionSynchronizationManager.getResource(getDataSource());
   txObject.setConnectionHolder(conHolder);
  }

  return txObject;
 }
 
代码6.
 protected void doBegin(Object transaction, TransactionDefinition definition) {
  HibernateTransactionObject txObject = (HibernateTransactionObject) transaction;

  if (txObject.hasConnectionHolder() && !txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
   throw new IllegalTransactionStateException(
     "Pre-bound JDBC Connection found! HibernateTransactionManager does not support " +
     "running within DataSourceTransactionManager if told to manage the DataSource itself. " +
     "It is recommended to use a single HibernateTransactionManager for all transactions " +
     "on a single DataSource, no matter whether Hibernate or JDBC access.");
  }

  Session session = null;

  try {
   if (txObject.getSessionHolder() == null || txObject.getSessionHolder().isSynchronizedWithTransaction()) {
    Interceptor entityInterceptor = getEntityInterceptor();
    // getSessionFactory().openSession() 见代码7.
    Session newSession = (entityInterceptor != null ?
      getSessionFactory().openSession(entityInterceptor) : getSessionFactory().openSession());
      
    if (logger.isDebugEnabled()) {
     logger.debug("Opened new Session [" + SessionFactoryUtils.toString(newSession) +
       "] for Hibernate transaction");
    }
    // 把事物对象session属性赋值。这里Session对象的connection = null
    txObject.setSession(newSession);
   }

   session = txObject.getSessionHolder().getSession();

   if (this.prepareConnection && isSameConnectionForEntireSession(session)) {
    // We're allowed to change the transaction settings of the JDBC Connection.
    if (logger.isDebugEnabled()) {
     logger.debug(
       "Preparing JDBC Connection of Hibernate Session [" + SessionFactoryUtils.toString(session) + "]");
    }
    // 从Session中建立连接,见代码8.
    Connection con = session.connection();
    // 根据条件是否执行con.setReadOnly(true),con.setTransactionIsolation(definition.getIsolationLevel())。然后返回previousIsolationLevel。
    Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
    txObject.setPreviousIsolationLevel(previousIsolationLevel);
   }
   else {
    // Not allowed to change the transaction settings of the JDBC Connection.
    if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
     // We should set a specific isolation level but are not allowed to...
     throw new InvalidIsolationLevelException(
       "HibernateTransactionManager is not allowed to support custom isolation levels: " +
       "make sure that its 'prepareConnection' flag is on (the default) and that the " +
       "Hibernate connection release mode is set to 'on_close' (SpringTransactionFactory's default). " +
       "Make sure that your LocalSessionFactoryBean actually uses SpringTransactionFactory: Your " +
       "Hibernate properties should *not* include a 'hibernate.transaction.factory_class' property!");
    }
    if (logger.isDebugEnabled()) {
     logger.debug(
       "Not preparing JDBC Connection of Hibernate Session [" + SessionFactoryUtils.toString(session) + "]");
    }
   }
   
   // 如果是definition.isReadOnly() = TRUE ,为查询,所以不用与DB同步。因为不涉及更新DB内容。
   if (definition.isReadOnly() && txObject.isNewSession()) {
    // Just set to NEVER in case of a new Session for this transaction.
    session.setFlushMode(FlushMode.NEVER);
   }

   // 如果是txObject.isNewSession() = TRUE ,则不用设置session.setFlushMode(FlushMode.AUTO),因为新Session 默认就是FlushMode.AUTO。
   if (!definition.isReadOnly() && !txObject.isNewSession()) {
    // We need AUTO or COMMIT for a non-read-only transaction.
    FlushMode flushMode = session.getFlushMode();
    if (flushMode.lessThan(FlushMode.COMMIT)) {
     session.setFlushMode(FlushMode.AUTO);
     txObject.getSessionHolder().setPreviousFlushMode(flushMode);
    }
   }

   Transaction hibTx = null;

   // Register transaction timeout.
   int timeout = determineTimeout(definition);
   if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
    // Use Hibernate's own transaction timeout mechanism on Hibernate 3.1
    // Applies to all statements, also to inserts, updates and deletes!
    hibTx = session.getTransaction();
    hibTx.setTimeout(timeout);
    hibTx.begin();
   }
   else {
    // Open a plain Hibernate transaction without specified timeout.
    //打开连接,设置con.setAutoCommit(false),开启事物,去掉自动提交。见代码9.
    hibTx = session.beginTransaction();
   }

   // Add the Hibernate transaction to the session holder.
   txObject.getSessionHolder().setTransaction(hibTx);

   // Register the Hibernate Session's JDBC Connection for the DataSource, if set.
   if (getDataSource() != null) {
    //这里通过session打开的连接是同一个连接
    Connection con = session.connection();
    ConnectionHolder conHolder = new ConnectionHolder(con);
    if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
     conHolder.setTimeoutInSeconds(timeout);
    }
    if (logger.isDebugEnabled()) {
     logger.debug("Exposing Hibernate transaction as JDBC transaction [" + con + "]");
    }
    // Bind the Connection holder to the thread.
    TransactionSynchronizationManager.bindResource(getDataSource(), conHolder);
    txObject.setConnectionHolder(conHolder);
   }

   // Bind the session holder to the thread.
   if (txObject.isNewSessionHolder()) {
    TransactionSynchronizationManager.bindResource(getSessionFactory(), txObject.getSessionHolder());
   }
   txObject.getSessionHolder().setSynchronizedWithTransaction(true);
  }

  catch (Exception ex) {
   if (txObject.isNewSession()) {
    try {
     if (session.getTransaction().isActive()) {
      session.getTransaction().rollback();
     }
    }
    catch (Throwable ex2) {
     logger.debug("Could not rollback Session after failed transaction begin", ex);
    }
    finally {
     SessionFactoryUtils.closeSession(session);
    }
   }
   throw new CannotCreateTransactionException("Could not open Hibernate Session for transaction", ex);
  }
 }

代码7. SessionFactoryImpl

 public org.hibernate.classic.Session openSession() throws HibernateException {
  return openSession(interceptor);
 }
 
 public org.hibernate.classic.Session openSession(Interceptor sessionLocalInterceptor) throws HibernateException {
  // note that this timestamp is not correct if the connection provider
  // returns an older JDBC connection that was associated with a
  // transaction that was already begun before openSession() was called
  // (don't know any possible solution to this!)
  
  long timestamp = settings.getRegionFactory().nextTimestamp();
  return openSession( null, true, timestamp, sessionLocalInterceptor );
 }
 
 private SessionImpl openSession(Connection connection,boolean autoClose,long timestamp,Interceptor sessionLocalInterceptor) {
  return new SessionImpl(
          connection,
          this,
          autoClose,
          timestamp,
          sessionLocalInterceptor == null ? interceptor : sessionLocalInterceptor,
          settings.getDefaultEntityMode(),
          settings.isFlushBeforeCompletionEnabled(),
          settings.isAutoCloseSessionEnabled(),
          settings.getConnectionReleaseMode()
   );
 }
从这里看出由SessionFactory每次都是产生一个新的Session对象(SessionImpl)。

代码8. SessionImpl.java

 public Connection connection() throws HibernateException {
  errorIfClosed();
  return jdbcContext.borrowConnection();
 }
 
 // JDBCContext.java
 public Connection borrowConnection() {
  return connectionManager.borrowConnection();
 }
 
 // ConnectionManager.java
 public Connection borrowConnection() {
  if ( isClosed ) {
   throw new HibernateException( "connection manager has been closed" );
  }
  if ( isSuppliedConnection() ) {
   return connection;
  }
  else {
   if ( borrowedConnection == null ) {
    borrowedConnection = BorrowedConnectionProxy.generateProxy( this );
   }
   return borrowedConnection;
  }
 }
 // BorrowedConnectionProxy.java
 /**
  * Generates a Connection proxy wrapping the connection managed by the passed
  * connection manager.
  *
  * @param connectionManager The connection manager to wrap with the
  * connection proxy.
  * @return The generated proxy.
  */
 public static Connection generateProxy(ConnectionManager connectionManager) {
  BorrowedConnectionProxy handler = new BorrowedConnectionProxy( connectionManager );
  return ( Connection ) Proxy.newProxyInstance(
    getProxyClassLoader(),
          PROXY_INTERFACES, // private static final Class[] PROXY_INTERFACES = new Class[] { Connection.class, ConnectionWrapper.class };
          handler
  );
 }
 
代码9.
 // SessionImpl.java
 public Transaction beginTransaction() throws HibernateException {
  errorIfClosed();
  if ( rootSession != null ) {
   // todo : should seriously consider not allowing a txn to begin from a child session
   //      can always route the request to the root session...
   log.warn( "Transaction started on non-root session" );
  }
  Transaction result = getTransaction();
  result.begin();
  return result;
 }
 
 public Transaction getTransaction() throws HibernateException {
  errorIfClosed();
  return jdbcContext.getTransaction();
 }
 
  public Transaction getTransaction() throws HibernateException {
  if (hibernateTransaction==null) {
   hibernateTransaction = owner.getFactory().getSettings()
     .getTransactionFactory()
     .createTransaction( this, owner );
  }
  return hibernateTransaction; // org.hibernate.transaction.JDBCTransaction 见图JDBCContext.bmp
 }
 // JDBCTransaction.java
 public void begin() throws HibernateException {
  if (begun) {
   return;
  }
  if (commitFailed) {
   throw new TransactionException("cannot re-start transaction after failed commit");
  }

  log.debug("begin");

  try {
   toggleAutoCommit = jdbcContext.connection().getAutoCommit();
   if ( log.isDebugEnabled() ) {
    log.debug("current autocommit status: " + toggleAutoCommit);
   }
   if (toggleAutoCommit) {
    log.debug("disabling autocommit");
    jdbcContext.connection().setAutoCommit(false);   // 这里是关键,把事物提交模式改为手动
   }
  }
  catch (SQLException e) {
   log.error("JDBC begin failed", e);
   throw new TransactionException("JDBC begin failed: ", e);
  }

  callback = jdbcContext.registerCallbackIfNecessary();

  begun = true; // 事物开启
  committed = false; // 事物未提交
  rolledBack = false; // 事物未回滚

  // timeout>0 设置超时属性
  if ( timeout>0 ) {
   jdbcContext.getConnectionManager().getBatcher().setTransactionTimeout(timeout);
  }

  jdbcContext.afterTransactionBegin(this);
 }
 
代码10.
// HibernateTemplete.java

 public List find(String queryString) throws DataAccessException {
  return find(queryString, (Object[]) null);
 }
 
 public List find(final String queryString, final Object[] values) throws DataAccessException {
  return (List) executeWithNativeSession(new HibernateCallback() {
   public Object doInHibernate(Session session) throws HibernateException {
    Query queryObject = session.createQuery(queryString);
    prepareQuery(queryObject);
    if (values != null) {
     for (int i = 0; i < values.length; i++) {
      queryObject.setParameter(i, values[i]);
     }
    }
    return queryObject.list();
   }
  });
 }
 
 /**
  * Execute the action specified by the given action object within a
  * native {@link org.hibernate.Session}.
  * <p>This execute variant overrides the template-wide
  * {@link #isExposeNativeSession() "exposeNativeSession"} setting.
  * @param action callback object that specifies the Hibernate action
  * @return a result object returned by the action, or <code>null</code>
  * @throws org.springframework.dao.DataAccessException in case of Hibernate errors
  */
 public Object executeWithNativeSession(HibernateCallback action) {
  return doExecute(action, false, true);
 }
 
 /**
  * Execute the action specified by the given action object within a Session.
  * @param action callback object that specifies the Hibernate action
  * @param enforceNewSession whether to enforce a new Session for this template
  * even if there is a pre-bound transactional Session
  * @param enforceNativeSession whether to enforce exposure of the native
  * Hibernate Session to callback code
  * @return a result object returned by the action, or <code>null</code>
  * @throws org.springframework.dao.DataAccessException in case of Hibernate errors
  */
 protected Object doExecute(HibernateCallback action, boolean enforceNewSession, boolean enforceNativeSession)
   throws DataAccessException {

  Assert.notNull(action, "Callback object must not be null");
  // 这里执行getSession() 见代码11.
  Session session = (enforceNewSession ?
    SessionFactoryUtils.getNewSession(getSessionFactory(), getEntityInterceptor()) : getSession());
  
  // 是否存在事物,因为之前已经开启了事物,所以为true
  boolean existingTransaction = (!enforceNewSession &&
    (!isAllowCreate() || SessionFactoryUtils.isSessionTransactional(session, getSessionFactory())));
  if (existingTransaction) {
   logger.debug("Found thread-bound Session for HibernateTemplate");
  }

  FlushMode previousFlushMode = null;
  try {
   previousFlushMode = applyFlushMode(session, existingTransaction);
   enableFilters(session);
   Session sessionToExpose =
     (enforceNativeSession || isExposeNativeSession() ? session : createSessionProxy(session));
   
   // 执行具体的操作,见代码13.的片段
   /*
   * Query queryObject = session.createQuery(queryString);
   *  prepareQuery(queryObject);
   *  if (values != null) {
   *  for (int i = 0; i < values.length; i++) {
   *   queryObject.setParameter(i, values[i]);
   *  }
   *  }
   * return queryObject.list();
   *
   */
   Object result = action.doInHibernate(sessionToExpose);
   
   // 上面action.doInHibernate(sessionToExpose)对数据库操作完成后是否执行与DB的同步,见代码14.
   flushIfNecessary(session, existingTransaction);
   
   return result;  // 返回到代码15.,再返回到代码1.再返回到代码2.retVal = invocation.proceed(); 接着执行提交,关闭connection的操作。
  }
  catch (HibernateException ex) {
   throw convertHibernateAccessException(ex);
  }
  catch (SQLException ex) {
   throw convertJdbcAccessException(ex);
  }
  catch (RuntimeException ex) {
   // Callback code threw application exception...
   throw ex;
  }
  finally {
  
      // 存在事物,执行之,不关闭Session ,所以若在事务范围内多次执行HibernateTemplate及其回调函数,则使用的是同一个Session,同一个connection。
   if (existingTransaction) {
    logger.debug("Not closing pre-bound Hibernate Session after HibernateTemplate");
    disableFilters(session);
    if (previousFlushMode != null) {
     session.setFlushMode(previousFlushMode);
    }
   }
   else {
    // Never use deferred close for an explicitly new Session.
    if (isAlwaysUseNewSession()) {
     SessionFactoryUtils.closeSession(session);
    }
    else { // 如果不存在事物则关闭Session和connection
     SessionFactoryUtils.closeSessionOrRegisterDeferredClose(session, getSessionFactory());
    }
   }
  }
 }
 
代码11.
 // HibernateTemplete.java
 /**
  * Return a Session for use by this template.
  * <p>Returns a new Session in case of "alwaysUseNewSession" (using the same
  * JDBC Connection as a transactional Session, if applicable), a pre-bound
  * Session in case of "allowCreate" turned off, and a pre-bound or new Session
  * otherwise (new only if no transactional or otherwise pre-bound Session exists).
  * @return the Session to use (never <code>null</code>)
  * @see SessionFactoryUtils#getSession
  * @see SessionFactoryUtils#getNewSession
  * @see #setAlwaysUseNewSession
  * @see #setAllowCreate
  */
 protected Session getSession() {
  if (isAlwaysUseNewSession()) {
   return SessionFactoryUtils.getNewSession(getSessionFactory(), getEntityInterceptor());
  }
  else if (isAllowCreate()) { // 执行之,见代码12.
   return SessionFactoryUtils.getSession(
     getSessionFactory(), getEntityInterceptor(), getJdbcExceptionTranslator());
  }
  else if (SessionFactoryUtils.hasTransactionalSession(getSessionFactory())) {
   return SessionFactoryUtils.getSession(getSessionFactory(), false);
  }
  else {
   try {
    return getSessionFactory().getCurrentSession();
   }
   catch (HibernateException ex) {
    throw new DataAccessResourceFailureException("Could not obtain current Hibernate Session", ex);
   }
  }
 }
 
代码12.
 // SessionFactoryUtils.java
 /**
  * Get a Hibernate Session for the given SessionFactory. Is aware of and will
  * return any existing corresponding Session bound to the current thread, for
  * example when using {@link HibernateTransactionManager}. Will always create
  * a new Session otherwise.
  * <p>Supports setting a Session-level Hibernate entity interceptor that allows
  * to inspect and change property values before writing to and reading from the
  * database. Such an interceptor can also be set at the SessionFactory level
  * (i.e. on LocalSessionFactoryBean), on HibernateTransactionManager, or on
  * HibernateInterceptor/HibernateTemplate.
  * @param sessionFactory Hibernate SessionFactory to create the session with
  * @param entityInterceptor Hibernate entity interceptor, or <code>null</code> if none
  * @param jdbcExceptionTranslator SQLExcepionTranslator to use for flushing the
  * Session on transaction synchronization (may be <code>null</code>; only used
  * when actually registering a transaction synchronization)
  * @return the Hibernate Session
  * @throws DataAccessResourceFailureException if the Session couldn't be created
  * @see LocalSessionFactoryBean#setEntityInterceptor
  * @see HibernateInterceptor#setEntityInterceptor
  * @see HibernateTemplate#setEntityInterceptor
  */
 public static Session getSession(
   SessionFactory sessionFactory, Interceptor entityInterceptor,
   SQLExceptionTranslator jdbcExceptionTranslator) throws DataAccessResourceFailureException {

  try {
   return doGetSession(sessionFactory, entityInterceptor, jdbcExceptionTranslator, true);
  }
  catch (HibernateException ex) {
   throw new DataAccessResourceFailureException("Could not open Hibernate Session", ex);
  }
 }
 
 /**
  * Get a Hibernate Session for the given SessionFactory. Is aware of and will
  * return any existing corresponding Session bound to the current thread, for
  * example when using {@link HibernateTransactionManager}. Will create a new
  * Session otherwise, if "allowCreate" is <code>true</code>.
  * <p>Same as {@link #getSession}, but throwing the original HibernateException.
  * @param sessionFactory Hibernate SessionFactory to create the session with
  * @param entityInterceptor Hibernate entity interceptor, or <code>null</code> if none
  * @param jdbcExceptionTranslator SQLExcepionTranslator to use for flushing the
  * Session on transaction synchronization (may be <code>null</code>)
  * @param allowCreate whether a non-transactional Session should be created
  * when no transactional Session can be found for the current thread
  * @return the Hibernate Session
  * @throws HibernateException if the Session couldn't be created
  * @throws IllegalStateException if no thread-bound Session found and
  * "allowCreate" is <code>false</code>
  */
 private static Session doGetSession(
   SessionFactory sessionFactory, Interceptor entityInterceptor,
   SQLExceptionTranslator jdbcExceptionTranslator, boolean allowCreate)
   throws HibernateException, IllegalStateException {

  Assert.notNull(sessionFactory, "No SessionFactory specified");
        // 从当前线程中取得sessionHolder(在开启事物时绑定的)
  SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
  if (sessionHolder != null && !sessionHolder.isEmpty()) {
   // pre-bound Hibernate Session
   Session session = null;
   
   if (TransactionSynchronizationManager.isSynchronizationActive() && sessionHolder.doesNotHoldNonDefaultSession()) {
    // Spring transaction management is active ->
    // register pre-bound Session with it for transactional flushing.
    
    session = sessionHolder.getValidatedSession();
    if (session != null && !sessionHolder.isSynchronizedWithTransaction()) {
     logger.debug("Registering Spring transaction synchronization for existing Hibernate Session");
     TransactionSynchronizationManager.registerSynchronization(
       new SpringSessionSynchronization(sessionHolder, sessionFactory, jdbcExceptionTranslator, false));
     sessionHolder.setSynchronizedWithTransaction(true);
     // Switch to FlushMode.AUTO, as we have to assume a thread-bound Session
     // with FlushMode.NEVER, which needs to allow flushing within the transaction.
     FlushMode flushMode = session.getFlushMode();
     if (flushMode.lessThan(FlushMode.COMMIT) &&
       !TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
      session.setFlushMode(FlushMode.AUTO);
      sessionHolder.setPreviousFlushMode(flushMode);
     }
    }
   }
   else {
    // No Spring transaction management active -> try JTA transaction synchronization.
    session = getJtaSynchronizedSession(sessionHolder, sessionFactory, jdbcExceptionTranslator);
   }
   if (session != null) {
    return session; // 从当前线程中取得sessionHolder,从sessionHolder取得Session返回。
   }
  }
        // 以下是Opening Hibernate Session
  logger.debug("Opening Hibernate Session");
  Session session = (entityInterceptor != null ?
    sessionFactory.openSession(entityInterceptor) : sessionFactory.openSession());

  // Use same Session for further Hibernate actions within the transaction.
  // Thread object will get removed by synchronization at transaction completion.
  if (TransactionSynchronizationManager.isSynchronizationActive()) {
   // We're within a Spring-managed transaction, possibly from JtaTransactionManager.
   logger.debug("Registering Spring transaction synchronization for new Hibernate Session");
   SessionHolder holderToUse = sessionHolder;
   if (holderToUse == null) {
    holderToUse = new SessionHolder(session);
   }
   else {
    holderToUse.addSession(session);
   }
   if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
    session.setFlushMode(FlushMode.NEVER);
   }
   TransactionSynchronizationManager.registerSynchronization(
     new SpringSessionSynchronization(holderToUse, sessionFactory, jdbcExceptionTranslator, true));
   holderToUse.setSynchronizedWithTransaction(true);
   if (holderToUse != sessionHolder) {
    TransactionSynchronizationManager.bindResource(sessionFactory, holderToUse);
   }
  }
  else {
   // No Spring transaction management active -> try JTA transaction synchronization.
   registerJtaSynchronization(session, sessionFactory, jdbcExceptionTranslator, sessionHolder);
  }

  // Check whether we are allowed to return the Session.
  if (!allowCreate && !isSessionTransactional(session, sessionFactory)) {
   closeSession(session);
   throw new IllegalStateException("No Hibernate Session bound to thread, " +
       "and configuration does not allow creation of non-transactional one here");
  }

  return session;
 }
 
代码13.
 //HibernateTemperature.java

 public List find(final String queryString, final Object[] values) throws DataAccessException {
  return (List) executeWithNativeSession(new HibernateCallback() {
   public Object doInHibernate(Session session) throws HibernateException {
    
    Query queryObject = session.createQuery(queryString);
    prepareQuery(queryObject);
    if (values != null) {
     for (int i = 0; i < values.length; i++) {
      queryObject.setParameter(i, values[i]);
     }
    }
    return queryObject.list();
   }
  });
 }

代码14.
 // HibernateTemperature.java
 /**
  * Flush the given Hibernate Session if necessary.
  * @param session the current Hibernate Session
  * @param existingTransaction if executing within an existing transaction
  * @throws HibernateException in case of Hibernate flushing errors
  */
 protected void flushIfNecessary(Session session, boolean existingTransaction) throws HibernateException {
  if (getFlushMode() == FLUSH_EAGER || (!existingTransaction && getFlushMode() != FLUSH_NEVER)) {
   logger.debug("Eagerly flushing Hibernate session");
   session.flush();
  }
 }
刷新数据是根据flush mode和是否存在事务共同判断的.
如果flush mode是FLUSH_EAGER,无论存不存在事务,都立即刷新。
如果不存在事务而flush mode是FLUSH_AUTO,则立即刷新,否则刷新操作将由事务控制。
如果存在事物,且flush mode是FLUSH_AUTO 则不刷新,使用spring + hibernate
的声明式事物就是这种情况,它是在最终提交的时候,自动刷新的(提交就会触发刷新事件)。

注意这里的刷新只是说明向DB发送SQL文,而如果此时恰好connection的autoCommit = true则DB才会响应此sql文的操作。而如果虽然执行了
刷新操作(即向DB发送SQL文),但connection的autoCommit = false则DB也不会响应此sql文的操作。另一种情况是,只要执行了
connection.commit()操作也会执行对数据可得
所以要注意大量数据的删除、更新,添加的情况。见《getHibernateTemperature()批处理》.

flushIfNecessary在HibernateTemplate的execute方法中被调用,HibernateTemplate提供的数据访问操作最终都是调用execute实现的。


代码15.  
  /**
   * Gives a marginal performance improvement versus using reflection to
   * invoke the target when invoking public methods.
   */
  protected Object invokeJoinpoint() throws Throwable {
   if (this.protectedMethod) {
    return super.invokeJoinpoint();
   }
   else {
    return this.methodProxy.invoke(this.target, this.arguments);
   }
  }
 }
 
代码16.
//TransactionAspectSupport.java

 /**
  * Execute after successful completion of call, but not after an exception was handled.
  * Do nothing if we didn't create a transaction.
  * @param txInfo information about the current transaction
  */
 protected void commitTransactionAfterReturning(TransactionInfo txInfo) {
  if (txInfo != null && txInfo.hasTransaction()) {
   if (logger.isTraceEnabled()) {
    logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]");
   }
   getTransactionManager().commit(txInfo.getTransactionStatus());
  }
 }
 
// AbstractPlatformTransactionManager.java
 /**
  * This implementation of commit handles participating in existing
  * transactions and programmatic rollback requests.
  * Delegates to <code>isRollbackOnly</code>, <code>doCommit</code>
  * and <code>rollback</code>.
  * @see org.springframework.transaction.TransactionStatus#isRollbackOnly()
  * @see #doCommit
  * @see #rollback
  */
 public final void commit(TransactionStatus status) throws TransactionException {
  if (status.isCompleted()) {
   throw new IllegalTransactionStateException(
     "Transaction is already completed - do not call commit or rollback more than once per transaction");
  }

  DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
  if (defStatus.isLocalRollbackOnly()) {
   if (defStatus.isDebug()) {
    logger.debug("Transactional code has requested rollback");
   }
   processRollback(defStatus);
   return;
  }
  if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
   if (defStatus.isDebug()) {
    logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
   }
   processRollback(defStatus);
   // Throw UnexpectedRollbackException only at outermost transaction boundary
   // or if explicitly asked to.
   if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
    throw new UnexpectedRollbackException(
      "Transaction rolled back because it has been marked as rollback-only");
   }
   return;
  }
       
  processCommit(defStatus);
 }
 
 /**
  * Process an actual commit.
  * Rollback-only flags have already been checked and applied.
  * @param status object representing the transaction
  * @throws TransactionException in case of commit failure
  */
 private void processCommit(DefaultTransactionStatus status) throws TransactionException {
  try {
   boolean beforeCompletionInvoked = false;
   try {
    prepareForCommit(status);
    triggerBeforeCommit(status);
    triggerBeforeCompletion(status);
    beforeCompletionInvoked = true;
    boolean globalRollbackOnly = false;
    if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
     globalRollbackOnly = status.isGlobalRollbackOnly();
    }
    if (status.hasSavepoint()) {
     if (status.isDebug()) {
      logger.debug("Releasing transaction savepoint");
     }
     status.releaseHeldSavepoint();
    }
    else if (status.isNewTransaction()) {
     if (status.isDebug()) {
      logger.debug("Initiating transaction commit");
     }
     doCommit(status);
    }
    // Throw UnexpectedRollbackException if we have a global rollback-only
    // marker but still didn't get a corresponding exception from commit.
    if (globalRollbackOnly) {
     throw new UnexpectedRollbackException(
       "Transaction silently rolled back because it has been marked as rollback-only");
    }
   }
   catch (UnexpectedRollbackException ex) {
    // can only be caused by doCommit
    triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
    throw ex;
   }
   catch (TransactionException ex) {
    // can only be caused by doCommit
    if (isRollbackOnCommitFailure()) {
     doRollbackOnCommitException(status, ex);
    }
    else {
     triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
    }
    throw ex;
   }
   catch (RuntimeException ex) {
    if (!beforeCompletionInvoked) {
     triggerBeforeCompletion(status);
    }
    doRollbackOnCommitException(status, ex);
    throw ex;
   }
   catch (Error err) {
    if (!beforeCompletionInvoked) {
     triggerBeforeCompletion(status);
    }
    doRollbackOnCommitException(status, err);
    throw err;
   }

   // Trigger afterCommit callbacks, with an exception thrown there
   // propagated to callers but the transaction still considered as committed.
   try {
    triggerAfterCommit(status);
   }
   finally {
    triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
   }

  }
  finally {
   cleanupAfterCompletion(status);
  }
 }
 
// HibernagteTransactionManager.java
 
 protected void doCommit(DefaultTransactionStatus status) {
  HibernateTransactionObject txObject = (HibernateTransactionObject) status.getTransaction();
  if (status.isDebug()) {
   logger.debug("Committing Hibernate transaction on Session [" +
     SessionFactoryUtils.toString(txObject.getSessionHolder().getSession()) + "]");
  }
  try {
   txObject.getSessionHolder().getTransaction().commit();
  }
  catch (org.hibernate.TransactionException ex) {
   // assumably from commit call to the underlying JDBC connection
   throw new TransactionSystemException("Could not commit Hibernate transaction", ex);
  }
  catch (HibernateException ex) {
   // assumably failed to flush changes to database
   throw convertHibernateAccessException(ex);
  }
 }

// JDBCTransaction.java
 public void commit() throws HibernateException {
  if (!begun) {
   throw new TransactionException("Transaction not successfully started");
  }

  log.debug("commit");

  if ( !transactionContext.isFlushModeNever() && callback ) {
   // 刷新与DB同步。
   transactionContext.managedFlush(); //if an exception occurs during flush, user must call rollback()
  }

  notifyLocalSynchsBeforeTransactionCompletion();
  if ( callback ) {
   jdbcContext.beforeTransactionCompletion( this );
  }

  try {
   // 1: 提交 2: autoCommit = true
   commitAndResetAutoCommit();
   
   log.debug("committed JDBC Connection");
   committed = true;
   if ( callback ) {
    jdbcContext.afterTransactionCompletion( true, this );
   }
   notifyLocalSynchsAfterTransactionCompletion( Status.STATUS_COMMITTED );
  }
  catch (SQLException e) {
   log.error("JDBC commit failed", e);
   commitFailed = true;
   if ( callback ) {
    jdbcContext.afterTransactionCompletion( false, this );
   }
   notifyLocalSynchsAfterTransactionCompletion( Status.STATUS_UNKNOWN );
   throw new TransactionException("JDBC commit failed", e);
  }
  finally {
   closeIfRequired();
  }
 }

 private void commitAndResetAutoCommit() throws SQLException {
  try {
   jdbcContext.connection().commit();
  }
  finally {
   toggleAutoCommit();
  }
 }
 
  private void toggleAutoCommit() {
  try {
   if (toggleAutoCommit) {
    log.debug("re-enabling autocommit");
    jdbcContext.connection().setAutoCommit( true );
   }
  }
  catch (Exception sqle) {
   log.error("Could not toggle autocommit", sqle);
   //swallow it (the transaction _was_ successful or successfully rolled back)
  }
 }
// SessionImpl.java
 public void managedFlush() {
  if ( isClosed() ) {
   log.trace( "skipping auto-flush due to session closed" );
   return;
  }
  log.trace("automatically flushing session");
  flush();
  
  if ( childSessionsByEntityMode != null ) {
   Iterator iter = childSessionsByEntityMode.values().iterator();
   while ( iter.hasNext() ) {
    ( (Session) iter.next() ).flush();
   }
  }
 }
 
 public void flush() throws HibernateException {
  errorIfClosed();
  checkTransactionSynchStatus();
  if ( persistenceContext.getCascadeLevel() > 0 ) {
   throw new HibernateException("Flush during cascade is dangerous");
  }
  FlushEventListener[] flushEventListener = listeners.getFlushEventListeners();
  for ( int i = 0; i < flushEventListener.length; i++ ) {
   flushEventListener[i].onFlush( new FlushEvent(this) );
  }
 }
--------------------------------------------------------------------
下面是javaEye上提出一个问题:Spring真的能mixed JDBC和ORM?
---mixed ORM and JDBC usage is a feature of Spring DAO
引文如下。
这是Rod Johnson在Spring官方上说的....以前我的理解是可以在同一个事务里
混合JDBC和ORM 编程(不考虑JTA).但我发现我错了..

比如有这样一个业务方法

void doBusiness(){
  doAction1(); 使用JdbcTemplete
  doAction2(); 使用JdoDaoSupport或HBDaoSupport
}

但这样缺是行不通的,Spring只是能拿到HB和JDBC的同一个数据源(DataSource),却拿不到Hibernate和Jdbc的同一个底层Connection的连接
导致这2个方法将分别完成,并不能处在同一个事务,而且我运行的后还出现连接未释放的情况.(他们单独拿出来当然是正确的)...

难道rod的话的意思只是可以在不同的方法里,使jdbc和ORM拿到同一个数据源,mixed JDBC and ORM ?????郁闷中...

一下是回答:
jdo dao和jdbc dao能否在同一个事务里这我不太清楚。因为我没用过jdo daosupport。
但是jdbc daosupport和hibernate daosupport却能被wrap到同一个事务里。成立需要几点条件:
1、使用同一个datasource
2、事务交由hibernateTransactionManager管理
3、相关dao以及service需要使用runtime exception体系,使用spring提供的exception可以,自己封装设计的runtime exception体系也行。

与此相关的事务代码片断在HibernateTransactionManager类中。最好可以把DatasourceTransactionManager和HibernateTransactionManager对比来看。
在此贴上几个源码片断,多余的我就不解释了。相信大家一看自明。
HibernateTransactionManager#doGetTransaction
Java代码
HibernateTransactionObject txObject = new HibernateTransactionObject();;  
txObject.setSavepointAllowed(isNestedTransactionAllowed(););;  
 
if (TransactionSynchronizationManager.hasResource(getSessionFactory(););); {  
    SessionHolder sessionHolder =  
            (SessionHolder); TransactionSynchronizationManager.getResource(getSessionFactory(););;  
    if (logger.isDebugEnabled();); {  
        logger.debug("Found thread-bound session [" + sessionHolder.getSession(); +  
                "] for Hibernate transaction");;  
    }  
    txObject.setSessionHolder(sessionHolder, false);;  
    if (getDataSource(); != null); {  
        ConnectionHolder conHolder = (ConnectionHolder);  
                TransactionSynchronizationManager.getResource(getDataSource(););;  
        txObject.setConnectionHolder(conHolder);;  
    }  
}  
 
return txObject; 

  HibernateTransactionObject txObject = new HibernateTransactionObject();;
  txObject.setSavepointAllowed(isNestedTransactionAllowed(););;

  if (TransactionSynchronizationManager.hasResource(getSessionFactory(););); {
   SessionHolder sessionHolder =
     (SessionHolder); TransactionSynchronizationManager.getResource(getSessionFactory(););;
   if (logger.isDebugEnabled();); {
    logger.debug("Found thread-bound session [" + sessionHolder.getSession(); +
      "] for Hibernate transaction");;
   }
   txObject.setSessionHolder(sessionHolder, false);;
   if (getDataSource(); != null); {
    ConnectionHolder conHolder = (ConnectionHolder);
      TransactionSynchronizationManager.getResource(getDataSource(););;
    txObject.setConnectionHolder(conHolder);;
   }
  }

  return txObject;

由此可以看出hibernateTransactionManager可以检测到绑定在当前线程上的connection

HibernateTransactionManager#doBegin
Java代码
Connection con = session.connection();;  
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);;  
txObject.setPreviousIsolationLevel(previousIsolationLevel);;  
 
...........  
 
if (getDataSource(); != null); {  
    ConnectionHolder conHolder = new ConnectionHolder(con);;  
    if (definition.getTimeout(); != TransactionDefinition.TIMEOUT_DEFAULT); {  
        conHolder.setTimeoutInSeconds(definition.getTimeout(););;  
    }  
    if (logger.isDebugEnabled();); {  
        logger.debug("Exposing Hibernate transaction as JDBC transaction [" +  
                conHolder.getConnection(); + "]");;  
    }  
    TransactionSynchronizationManager.bindResource(getDataSource();, conHolder);;  
    txObject.setConnectionHolder(conHolder);;  
}  
// bind the session holder to the thread  
if (txObject.isNewSessionHolder();); {  
    TransactionSynchronizationManager.bindResource(getSessionFactory();, txObject.getSessionHolder(););;  

   Connection con = session.connection();;
   Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);;
   txObject.setPreviousIsolationLevel(previousIsolationLevel);;

..............

   if (getDataSource(); != null); {
    ConnectionHolder conHolder = new ConnectionHolder(con);;
    if (definition.getTimeout(); != TransactionDefinition.TIMEOUT_DEFAULT); {
     conHolder.setTimeoutInSeconds(definition.getTimeout(););;
    }
    if (logger.isDebugEnabled();); {
     logger.debug("Exposing Hibernate transaction as JDBC transaction [" +
       conHolder.getConnection(); + "]");;
    }
    TransactionSynchronizationManager.bindResource(getDataSource();, conHolder);;
    txObject.setConnectionHolder(conHolder);;
   }
   // bind the session holder to the thread
   if (txObject.isNewSessionHolder();); {
    TransactionSynchronizationManager.bindResource(getSessionFactory();, txObject.getSessionHolder(););;
   }

由此可以看出,在真正启动一个事务时,hbTxnManager会先把connection绑定到当前线程,再绑定session到当前线程,由TransactionSynchronizationManager统一管理。
并且上面提到的connection是从session中取得的,也就是说,无论是jdbc dao还是hibernate dao本质上使用的是同一个database connection

因此得出结论:HibernateTransactionManager实际上是可以同时管理由JdbcTemplate或JdbcDaoSupport实现的dao以及HibernateTemplate或HibernateDaoSupport实现的事务的。


Rod Johnson的话:

引用
It is possible--and sometimes useful--to have coordinated transactions for both. Your JDBC transactions will be managed by the
HibernateTransactionManager if you work with the same JDBC DataSource in the same transaction. That is, create the SessionFactory
 using Spring's SessionFactoryBean using the same DataSource that your JdbcTemplates use.

The only issue to watch, of course, is that you may be invalidating your Hibernate cache by JDBC changes. Generally I find it best
 to use JDBC to update only tables that don't have Hibernate mappings.

 

Juergen Hoeller的话:

引用
As Rod said, simply keep using HibernateTransactionManager, which auto-detects the DataSource used by Hibernate and seamlessly exposes
 Hibernate transactions as JDBC transactions for that DataSource. JDBC code that accesses the same DataSource via Spring will automatically
  participate in such transactions.

Note that you must specify the DataSource for Hibernate via LocalSessionFactoryBean's "dataSource" property to allow HibernateTransactionManager
 to auto-detect it. Alternatively, you can explicitly pass the DataSource to HibernateTransactionManager's "dataSource" property.
 
实际经过我的测试也是可以的。
----------------------------------------------------------------
另一个帖子引文如下:

Hibernate与JDBC事务整合
 
最近摸索了一下Hibernate与JDBC(iBATIS)事务整合问题,算是有点收获,写出来和大家讨论一下吧。。。
一般大家都会使用Spring声明型事务 transactionAttributes 为 PROPAGATION_REQUIRED
Hibernate 使用 HibernateTransactionManager 、JDBC(iBATIS) 使用 DataSourceTransactionManager
当需要将它们整合到一个事务中的时候,普通的做法是配置统一的DataSource, Hibernate与JDBC(iBATIS)
都使用HibernateTransactionManager

坛子里有篇帖子已经详细论述了这种整合方案 http://www.javaeye.com/topic/11063?page=2

-----------------

Hibernate与JDBC(iBATIS) 都使用DataSourceTransactionManager又可不可以呢?

普遍的看法是 NO! 认为DataSourceTransactionManager根本就不处理Hibernate的session,事务当然无法实现。。。

但事实是否真的如此呢?

答案同样是 NO! http://forum.springframework.org/archive/index.php/t-9878.html

Juergen Hoeller:
Just if you proxy your JDBC DataSource with a TransactionAwareDataSourceProxy (available since Spring 1.1 RC1) and pass that proxy to your LocalSessionFactoryBean, you could use DataSourceTransactionManager in combination with Hibernate.

也就是说配置 sessionFactory  的 useTransactionAwareDataSource 为 true
Hibernate与JDBC(iBATIS)  都使用 DataSourceTransactionManager 同样可以保证事务
原理就是保证了 connection 的唯一性。

以上我也测试时可以的。以上开始我举的例子就用了JdbcTemplates和getHibernateTemplate()结合的方法。

一下引自《Spring Framework 开发参考手册》9.4.2. 低层次方案
<-----------
在较低层次上,有以下这些类:DataSourceUtils(针对JDBC),SessionFactoryUtils(针对Hibernate),
PersistenceManagerFactoryUtils(针对JDO)等等。当对应用代码来说,直接同原始持久化API特有的资源类型打
交道是更好的选择时,这些类确保应用代码获取到正确的Spring框架所管理的bean,事务被正确同步,处理过程中的异
常被映射到一致的API。
例如,在JDBC环境下,你不再使用传统的调用 DataSource 的 getConnection() 方法的方式,而是使用Spring的
org.springframework.jdbc.datasource.DataSourceUtils,像这样:

Connection conn = DataSourceUtils.getConnection(dataSource);
如果已有一个事务及与之关联的connection存在,该实例将被返回。否则,该方法调用将触发起一个新的connection的
创建动作,该connection(可选地)被同步到任何现有的事务,并可以在同一事务范围内被后续的调用复用。正如上面提到
的,这个过程有一个额外的好处,就是任何 SQLException将被包装为Spring框架的 CannotGetJdbcConnectionException,
该类是Spring框架的unchecked的DataAccessExceptions层次体系中的一员。这将给你比从 SQLException 中简单所得
更多的信息,而且保证了跨数据库——甚至其他持久化技术——的移植性。

应该指出的是,这些类同样可以在没有Spring事务管理的环境中工作良好(事务同步能力是可选的),所以无论你是否使用Spring
的事务管理,你都可以使用这些类。

当然,一旦你用过Spring的JDBC支持或Hibernate支持,你一般就不再会选择 DataSourceUtils 或是别的辅助类了,因为你
会更乐意与Spring抽象一起工作,而不是直接使用相关的API。例如,如果你使用Spring的 JdbcTemplate 或 jdbc.object
包来简化使用JDBC,Spring会在幕后替你正确地获取连接,而你不需要写任何特殊代码。
----------- >

 
-------------------------------------------------------------------------

引入网文http://soft6.com/tech/13/133957.html
关键词:HibernateTemplate session.flush() session.connection().setAutoCommit(false)

目的:使用HibernateTemplate执行execute(new HibernateCallback())方法,从HibernateCallback中得到session,
在此session中做多个操作,在没有配置事务的情况下,希望这些操作位于同一个事务中。
如果你这样写(1):

public static void main(String ss[]) {
CtxUtil.getBaseManager().getHibernateTemplate().execute(new HibernateCallback() {
public Object doInHibernate(Session session) throws HibernateException, SQLException {
// 保存stu1
Student stu1 = new Student();
stu1.setName("aaaa");// 在数据库中,name字段不允许为null
session.save(stu1);
session.flush();//实际上,如果不是程序员"手痒"来调用这个flush(),HibernateTemplate中session的事务处理还是很方便的

Student stu2 = new Student();
session.save(stu2);// 没有设置name字段,预期会报出例外
session.flush();
return null;
}
});

}

(在没有配置声明式事务的情况下)你期望spring在执行完execute回调后,在关闭session的时候提交事务,想法是很好的,但spring并不会这么做。
让我们来看看在Hibernate的源代码中,session.beginTransation()做了什么事。看如下代码(2):

public Transaction beginTransaction() throws HibernateException {
errorIfClosed();
if ( rootSession != null ) {
// todo : should seriously consider not allowing a txn to begin from a child session
// can always route the request to the root session
log.warn( "Transaction started on non-root session" );
}
Transaction result = getTransaction();
result.begin();
return result;
}

这个方法中的result是一个org.hibernate.transaction.JDBCTransaction实例,而方法中的getTransaction()
方法源代码为(3):

public Transaction getTransaction() throws HibernateException {
if (hibernateTransaction==null) {
log.error(owner.getFactory().getSettings()
.getTransactionFactory().getClass());
hibernateTransaction = owner.getFactory().getSettings()
.getTransactionFactory()
.createTransaction( this, owner );
}
return hibernateTransaction;
}

再次追踪,owner.getFactory()。getSettings() .getTransactionFactory()的createTransaction()方法
源代码如下(4):

public Transaction createTransaction(JDBCContext jdbcContext, Context transactionContext)
throws HibernateException {
return new JDBCTransaction( jdbcContext, transactionContext );
}

它返回了一个JDBCTransaction,没什么特别的。

在代码2中,执行了result.begin(),其实也就是JDBCTransaction实例的begin()方法,来看看(5):

public void begin() throws HibernateException {
if (begun) {
return;
}
if (commitFailed) {
throw new TransactionException("cannot re-start transaction after failed commit");
}
log.debug("begin");
try {
toggleAutoCommit = jdbcContext.connection().getAutoCommit();
if (log.isDebugEnabled()) {
log.debug("current autocommit status: " + toggleAutoCommit);
}
if (toggleAutoCommit) {
log.debug("disabling autocommit");
jdbcContext.connection().setAutoCommit(false);//把自动提交设为了false
}
} catch (SQLException e) {
log.error("JDBC begin failed", e);
throw new TransactionException("JDBC begin failed: ", e);
}
callback = jdbcContext.registerCallbackIfNecessary();
begun = true;
committed = false;
rolledBack = false;

if (timeout > 0) {
jdbcContext.getConnectionManager().getBatcher().setTransactionTimeout(timeout);
}

jdbcContext.afterTransactionBegin(this);
}

在直接使用Hibernate时,要在事务结束的时候,写上一句:tx.commit(),这个commit()的源码为:

public void commit() throws HibernateException {
if (!begun) {
throw new TransactionException("Transaction not successfully started");
}

log.debug("commit");

if (!transactionContext.isFlushModeNever() && callback) {
transactionContext.managedFlush(); // if an exception occurs during flush, user must call rollback()
}

notifyLocalSynchsBeforeTransactionCompletion();
if (callback) {
jdbcContext.beforeTransactionCompletion(this);
}

try {
commitAndResetAutoCommit();//重点代码,它的作用是提交事务,并把connection的autocommit属性恢复为true
log.debug("committed JDBC Connection");
committed = true;
if (callback) {
jdbcContext.afterTransactionCompletion(true, this);
}
notifyLocalSynchsAfterTransactionCompletion(Status.STATUS_COMMITTED);
} catch (SQLException e) {
log.error("JDBC commit failed", e);
commitFailed = true;
if (callback) {
jdbcContext.afterTransactionCompletion(false, this);
}
notifyLocalSynchsAfterTransactionCompletion(Status.STATUS_UNKNOWN);
throw new TransactionException("JDBC commit failed", e);
} finally {
closeIfRequired();
}
}

上面代码中,commitAndResetAutoCommit()方法的源码如下:

private void commitAndResetAutoCommit() throws SQLException {
try {
jdbcContext.connection().commit();//这段不用说也能理解了
} finally {
toggleAutoCommit();//这段的作用是恢复connection的autocommit属性为true
}
}

上述代码的toggleAutoCommit()源代码如下:

private void toggleAutoCommit() {
try {
if (toggleAutoCommit) {
log.debug("re-enabling autocommit");
jdbcContext.connection().setAutoCommit(true);//这行代码的意义很明白了吧
}
} catch (Exception sqle) {
log.error("Could not toggle autocommit", sqle);
}
}

因此,如果你是手动管理它的session,并手动开启事务关闭事务的话,完全可以保证你的事务(好像完全是废话)。


但是,如果你没有开启事物,因为在使用代码1时,spring中得到session的方式如下:
Session session = (entityInterceptor != null ? sessionFactory.openSession(entityInterceptor) :
sessionFactory。openSession());简单地说它就是得到了一个session,而没有对connection的autocommit()作任何操作,
spring管理范围内的session所持有的connection是autocommit=true的,spring借助这个属性,在它关闭session时,提交数据库事务。
因此如果你在源代码1中加上一句话:

public static void main(String ss[]) {
CtxUtil.getBaseManager().getHibernateTemplate().execute(new HibernateCallback() {
public Object doInHibernate(Session session) throws HibernateException, SQLException {
log.info(session.connection().getAutoCommit());//打印一下事务提交方式
// 保存stu1
Student stu1 = new Student();
stu1.setName("aaaa");// 在数据库中,name字段不允许为null
session.save(stu1);
session.flush();

Student stu2 = new Student();
session.save(stu2);// 没有设置name字段,预期会报出例外
session.flush();
return null;
}
});

}

运行后,它打出的结果是true,也就是说,虽然保存stu2时会报出例外,但如果commit属性为true,则每一个到达数据库的sql语句会立即被提交。
换句话说,在调用完session.save(stu1)后,调用session.flush(),会发送sql语句到数据库,再根据commit属性为true,则保存stu1
的操作已经被持久到数据库了,尽管后面的一条insert语句出了问题。

因此,如果你想在HibernateCallback中保持事务性,需要如下写:

public static void main(String ss[]) {
CtxUtil.getBaseManager().getHibernateTemplate().execute(new HibernateCallback() {
public Object doInHibernate(Session session) throws HibernateException, SQLException {
session.connection().setAutoCommit(false);
//保存stu1
Student stu1=new Student();
stu1.setName("aaaa");//在数据库中,name字段不允许为null
session.save(stu1);
session.flush();

Student stu2 = new Student();
session.save(stu2);//没有设置name字段,预期会报出例外
session.flush();
session.connection().commit();
//至于session的关闭就不用我们操心了
return null;
}
});

}

运行上述代码,没问题了。至此,可能有些读者早就对代码1不满意了:为什么每次save()以后要调用flush()?这是有原因的。
下面我们来看看把session.flush()去掉后会出什么问题。改掉后的代码如下:

public static void main(String ss[]) {
CtxUtil.getBaseManager().getHibernateTemplate().execute(new HibernateCallback() {
public Object doInHibernate(Session session) throws HibernateException, SQLException {
session.connection().setAutoCommit(false);
// 保存stu1
Student stu1 = new Student();
stu1.setName("aaaa");// 在数据库中,name字段不允许为null
session.save(stu1);
// session.flush();

Student stu2 = new Student();
session.save(stu2);// 没有设置name字段,预期会报出例外
// session.flush();
session.connection().commit();
return null;
}
});

}

运行上述代码,后台报数据库的not null错误,这个是合理的,打开数据库,没有发现新增记录,这个也是合理的。你可能会说:
由于事务失败,数据库当然不可能会有任何新增记录。好吧,我们再把代码改一下,去除not null的错误,以确保它能正常运行。代码如下:

public static void main(String ss[]) {
CtxUtil.getBaseManager().getHibernateTemplate().execute(new HibernateCallback() {
public Object doInHibernate(Session session) throws HibernateException, SQLException {
session.connection().setAutoCommit(false);
// 保存stu1
Student stu1 = new Student();
stu1.setName("aaaa");// 在数据库中,name字段不允许为null
session.save(stu1);
// session.flush();

Student stu2 = new Student();
stu2.setName("asdfasdf");//好了,这个字段设过值,不会再报not null错误了
session.save(stu2);
// session.flush();
session.connection().commit();
return null;
}
});

}

至此再运行上述代码,出现了一个奇怪的问题:虽然控制台把insert语句打出来了,但是:数据库没有出现任何新的记录。

究其原因,有二:

这里要注意个执行顺序问题:是先session.connection().commit()而此时没有flush sql,然后flushIfNecessary()和
closeSessionOrRegisterDeferredClose()但却没有commit。
所以
一、 session.connection().commit()确实导致数据库事务提交了,但是此刻session并没有向数据库发送任何语句。

二、 在spring后继的flushIfNecessary()和closeSessionOrRegisterDeferredClose()方法中,
第一个方法向数据库发送sql语句,第二个方法关闭session,同时关闭connection,然而问题在于:connection
已经在程序中被手动设置为auttocommit=false了,因此在关闭数据库时,也不会提交事务。

 

解决这个问题很容易,在程序中手动调用session.flush()就可以了。如下代码:

public static void main(String ss[]) {
CtxUtil.getBaseManager().getHibernateTemplate().execute(new HibernateCallback() {
public Object doInHibernate(Session session) throws HibernateException, SQLException {
session.connection().setAutoCommit(false);

//保存stu1
Student stu1=new Student();
stu1.setName("aaaa");//在数据库中,name字段不允许为null
session.save(stu1);

Student stu2 = new Student();
session.save(stu2);//没有设置name字段,预期会报出例外

session.flush();//向数据库发送sql
session.connection().commit();
return null;
}
});

}

运行上述代码,打开数据库查看,没有新增任何记录。在代码中新加一行stu2.setName("aaa");再次运行代码,发现数据库表中多了两条
记录。事务操作成功。

至此,虽然操作成功,但事情还没有结束。这是因为spring在调用doInHibernate()的后继的步骤中,还要进行flushIfNecessary()
操作,这个操作其实最后调用的还是session.flush()。因为在程序中已经手动调用过session.flush(),所以由spring调用的
session.flush()并不会对数据库发送sql(因为脏数据比对的原因)。虽然不会对结果有什么影响,但是多调了一次flush(),还是会
对性能多少有些影响。能不能控制让spring不调用session.flush()呢?可以的,只要加上一句代码,如下所示:

public static void main(String ss[]) {
CtxUtil.getBaseManager().getHibernateTemplate().setFlushMode(0);//0也就是FLUSH_NEVER
CtxUtil.getBaseManager().getHibernateTemplate().execute(new HibernateCallback() {
public Object doInHibernate(Session session) throws HibernateException, SQLException {
session.connection().setAutoCommit(false);

//保存stu1
Student stu1=new Student();
stu1.setName("aaaa");//在数据库中,name字段不允许为null
session.save(stu1);

Student stu2 = new Student();
stu2.setName("sdf");
session.save(stu2);//没有设置name字段,预期会报出例外

session.flush();
session.connection().commit();
return null;
}
});

}

通过设置HibernateTemplate的flushMode=FLUSH_NEVER来通知spring不进行session.flush()的调用,则spring的
flushIfNecessary()将不进行任何操作,它的flushIfNecessary()源代码如下:

protected void flushIfNecessary(Session session, boolean existingTransaction) throws HibernateException {
if (getFlushMode() == FLUSH_EAGER || (!existingTransaction && getFlushMode() != FLUSH_NEVER)) {
logger.debug("Eagerly flushing Hibernate session");
session.flush();
}
}

至此,代码1中的main()终于修改完毕。但事实上,这样的操作无疑是比较麻烦的,因此如果在spring中想利用session进行事务操作时,
最好还是用TransactionTemplate(编程式事务)或是声明式事务比较方便一些。

本例通过这么一个虽然简单但又绕来绕去的例子,主要是说明hibernate事务的一些内在特性,以及HibernateTemplate中如何处理session
和事务的开关,让读者对HibernateTemplate的源代码处理细节有一些了解,希望能给读者有抛砖引玉的作用。

 

原创粉丝点击