`

声明式事务管理

阅读更多
我们一下说的都是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
分享到:
评论

相关推荐

    Xml文件配置实现声明式事务管理

    在Spring框架中,声明式事务管理是一种非常重要的特性,它允许开发者通过XML配置或注解来定义事务的边界,而无需在业务代码中显式地管理事务。这种方式极大地提高了代码的可读性和可维护性。本篇文章将深入探讨如何...

    全面分析 Spring 的编程式事务管理及声明式事务管理

    本文将全面分析Spring中的编程式事务管理和声明式事务管理,旨在帮助开发者深入理解这两种事务管理方式,并在实际项目中合理选择。 **编程式事务管理** 编程式事务管理是通过代码直接控制事务的开始、提交、回滚等...

    spring声明式事务管理配置方式

    在Spring框架中,声明式事务管理是实现事务处理的一种高效且灵活的方式,它允许开发者通过在服务层方法上添加特定的注解来控制事务的边界,而无需编写大量的事务管理代码。这种方式使得业务逻辑和事务控制得以分离,...

    注解实现声明式事务管理

    在Spring框架中,注解是实现声明式事务管理的主要手段之一。相较于编程式事务管理,声明式事务管理更易于维护,因为事务管理的逻辑被声明在配置或元数据中,而不是散落在业务代码中。本篇文章将深入探讨如何使用注解...

    基于java的企业级应用开发:声明式事务管理.ppt

    在Spring框架中,声明式事务管理是实现事务控制的重要手段,它使得开发者无需在代码中显式地管理事务,极大地提高了代码的可维护性和可读性。本篇内容主要涉及两种声明式事务管理方式:基于XML和基于Annotation。 1...

    全面分析_Spring_的编程式事务管理及声明式事务管理.

    本篇文章将深入探讨Spring中的两种主要事务管理方式:编程式事务管理和声明式事务管理。 1. 编程式事务管理: 编程式事务管理允许开发者直接在代码中控制事务的开始、提交、回滚等操作。这种方式具有较高的灵活性,...

    全面分析_Spring_的编程式事务管理及声明式事务管理

    本教程将深入探讨 Spring 的编程式事务管理和声明式事务管理,帮助你理解这两种方式的差异与应用场景。 首先,编程式事务管理依赖于编程的方式显式地控制事务的开始、提交、回滚等操作。它通过实现 `...

    springmvc+mybatis+声明式事务管理

    在IT行业中,SpringMVC、MyBatis以及声明式事务管理是Java Web开发中的关键组件。这个项目结合了这三个核心技术,构建了一个高效、稳定的后端系统。以下将详细阐述这些技术及其相互配合的工作原理。 首先,...

    spring编程式和声明式事务管理

    它分为两种主要类型:编程式事务管理和声明式事务管理。这两种方式各有特点,适用于不同的场景。 首先,编程式事务管理是通过编写代码来控制事务的开始、提交、回滚以及异常处理。在Spring中,我们通常使用`...

    spring3,hibernate4 配置声明式事务管理(annotation方式)

    本篇将详细介绍如何在Spring 3和Hibernate 4中通过注解来实现声明式事务管理。 首先,我们需要在项目中引入Spring和Hibernate的依赖库。这通常通过Maven或Gradle等构建工具完成,确保添加了相应的依赖项。 接着,...

    全面分析Spring的编程式事务管理与声明式事务管理.doc

    全面分析 Spring 的编程式事务管理与声明式事务管理 本文将从 Spring 的事务管理入手,深入讲解编程式事务管理和声明式事务管理的实现机制和原理。通过本文的学习,您将能够理解 Spring 事务管理的本质,并灵活运用...

    spring AOP(声明式事务管理)小程序

    在本“spring AOP(声明式事务管理)小程序”中,我们将深入探讨Spring AOP如何实现声明式事务管理,以及相关的通知类型。 1. **什么是声明式事务管理**: 声明式事务管理是相对于编程式事务管理而言的,后者需要在...

    Spring 2.5整合iBATIS 2.3并使用Spring的声明式事务管理

    7. **声明式事务管理**:Spring提供了声明式事务管理,我们可以在方法级别或类级别通过@Transactional注解来控制事务的边界。例如: ```java @Service public class UserService { @Autowired private UserMapper...

    MyBatis Spring声明式事务管理示例代码

    Spring的声明式事务管理是采用AOP(Aspect-Oriented Programming,面向切面编程)实现的。在编程式事务管理中,各事务处理代码实际上是相似的,这就造成了代码重复;而且编程式事务管理会造成事务管理代码和被管理的...

    spring声明式事务管理+jdbc+连接池.zip

    本资料包"spring声明式事务管理+jdbc+连接池.zip"显然是针对Spring框架在数据库操作方面的深入学习,特别是如何利用Spring进行声明式事务管理和JDBC操作,以及如何配置和使用数据库连接池。接下来,我们将详细探讨这...

    实验 spring 声明事务

    实验 "Spring 声明事务" 是 Java 高级编程中的一个重要环节,旨在让学生掌握 Spring 框架中声明式事务管理的配置和使用。在实际应用中,事务管理是确保数据一致性、完整性和可靠性的关键组件。Spring 提供了声明式...

    spring3、 hibernate4 配置声明式事务管理(annotation方式)

    本篇将详细介绍如何在Spring 3和Hibernate 4中配置声明式事务管理,采用注解方式。 一、Spring的事务管理 Spring提供了两种事务管理方式:编程式事务管理和声明式事务管理。编程式事务管理需要在代码中显式地调用...

Global site tag (gtag.js) - Google Analytics