`

Spring事物

阅读更多
Spring事物提供的好处:
1.Provides a consistent programming model across different transaction APIs such as JTA, JDBC, Hibernate, JPA, and JDO
根据不同的事物API,如JTA,JDBC,Hibernate,JPA,JDO,提供了一致的编程模式。
2.Supports declarative transaction management
  支持声明式事物管理
3.Provides a simpler API for programmatic transaction management than a number of complex transaction APIs such as JTA
  相比与一些负责的事物API如JTA,提供了一种简单安全事务管理API。
事物的分类:
Global Transactions(全局事务)
Global transactions have a significant downside, in that code needs to use JTA, and JTA is a cumbersome API to use (partly due to its exception model). Furthermore, a JTA UserTransaction normally needs to be sourced from JNDI: meaning that we need to use both JNDI and JTA to use JTA. Obviously all use of global transactions limits the reusability of application code, as JTA is normally only available in an application server environment. Previously, the preferred way to use global transactions was via EJB CMT (Container Managed Transaction): CMT is a form of declarative transaction management (as distinguished from programmatic transaction management). EJB CMT removes the need for transaction-related JNDI lookups - although of course the use of EJB itself necessitates the use of JNDI. It removes most of the need (although not entirely) to write Java code to control transactions. The significant downside is that CMT is tied to JTA and an application server environment. Also, it is only available if one chooses to implement business logic in EJBs, or at least behind a transactional EJB facade. The negatives around EJB in general are so great that this is not an attractive proposition, especially in the face of compelling alternatives for declarative transaction management.
全局的事物有一个严重的下滑趋势,使用全局事物的代码需要使用JTA,并且JTA是一个沉重的API,此外,JTA事物通常来源于JNDI,意味着我们要使用JTA需要同时使用JNDI和JTA。很明显,所有全局事务的使用受限于应用代码的重用性。因此JTA通常只在一个应用服务环境中可用。之前全局事务受追捧是凭借EJB的CMT。CMT是一个声明式事物管理形式。EJB CVMT移除掉了对与事物相关的JDDI字典的依赖。尽管EJB本身的使用需要使用JNDI,但是他移除掉了大部分需要写java代码控制事物。严重下滑的趋势是由于CMT和JTA和一个应用服务环境相关。所以,当有一个需求去实现EJB下的商业逻辑时使用全局事物。或者至少基于EJB事物门面。EJB的副作用比较大通常也是EJB不是一个吸引人的论点。尤其在面临声明式事物管理的选择时。
Local Transactions(本地事物)
Local transactions may be easier to use, but have significant disadvantages: they cannot work across multiple transactional resources. For example, code that manages transactions using a JDBC connection cannot run within a global JTA transaction. Another downside is that local transactions tend to be invasive to the programming model.
Spring resolves these problems. It enables application developers to use a consistent programming model in any environment. You write your code once, and it can benefit from different transaction management strategies in different environments. The Spring Framework provides both declarative and programmatic transaction management. Declarative transaction management is preferred by most users, and is recommended in most cases.
With programmatic transaction management, developers work with the Spring Framework transaction abstraction, which can run over any underlying transaction infrastructure. With the preferred declarative model, developers typically write little or no code related to transaction management, and hence don't depend on the Spring Framework's transaction API (or indeed on any other transaction API).
本地事物可以更容易使用,但是有严重的缺点。不能跨多个事物工作。例如。使用JDBC管理事物的代码不能在一个全局的JTA事物中运行。另一个缺点是本地事物是一个侵入式的编程模式。
Spring解决了这些问题。他能是应用开发者在不同环境中使用同一个编程模式。你只需要写一次代码,它就能运行于不同环境的不同事物管理策略。Spring提供了声明式事物和编程式的事物。声明式编程事物管理是大多数使用者追捧的。并且被建议大多数情况下使用。
使用编程式事物管理,开发者使用Spring抽象事物。就可以运行在任何事物结构下。使用受欢迎的声明式事物。开发者可以写一点或是不写代码完成事物管理。因此不依赖于spring事物API。
Spring抽象事物的关键就是定义了一个事物策略。就是transtraction下的PlatformTransactionManager 接口。该接口一个包含3个抽象方法。
TransactionStatus getTransaction(TransactionDefinition definition)
    throws TransactionException;
  void commit(TransactionStatus status) throws TransactionException;
  void rollback(TransactionStatus status) throws TransactionException;
不管你是青睐于声明式事物还是编程式事物,定义正确的PlatformTransactionManager 的实现是必要的。
PlatformTransactionManager implementations normally require knowledge of the environment in which they work: JDBC, JTA, Hibernate
PlatformTransactionManager 的实现通常需要JDBC、JTA、Hibernate的工作环境。
下面定义一个JDBC数据源,然后使用DataSourceTransactionManager(PlatformTransactionManager 的一个实现类),并且给他一个指向新定义数据源的额引用。
JDBC事物实例如下:
数据源:
<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>
事物管理器:
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource"/>
</bean>
如果使用JTA事物,我们就使用容器的数据源凭借JNDI包含进来。结合JtaTransactionManager。JtaTransactionManager不需要了解数据源的信息,他将使用容器的全局事物管理结构。
实例:
<?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.5.xsd
     http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd">

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

  <bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager" />
 
  <!-- other <bean/> definitions here -->
</beans>
注意:The above definition of the 'dataSource' bean uses the <jndi-lookup/> tag from the 'jee' namespace.
     以上的数据源的定义使用的jee命名空间下的jndi-lookup标签
hiberante事物
hiberante事物的实现类是HibernateTransactionManager。实例如下。

<bean id="sessionFactory" class="org.springframework.orm.hibernate3.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.hibernate3.HibernateTransactionManager">
  <property name="sessionFactory" ref="sessionFactory" />
</bean>
注:hibernate需要引用上面定义的数据源,定义了sessionFactory,sessionFactory实际是一个缓存。可以缓存对象和查询结果(以sql为key,查询结果为value)。mappingResources包含了引入的表与实体映射文件。
hibernateProperties定义了相关的属性。如方言等。
如果使用hiberante或是其他和JTA,直接定义
<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>就行。
如果使用spring管理JDBC事物,可以使用一些工具类获取connection,如
Connection conn = DataSourceUtils.getConnection(dataSource);
声明式事物管理
Most users of the Spring Framework choose declarative transaction management. It is the option with the least impact on application code, and hence is most consistent with the ideals of a non-invasive lightweight container.
大多数spring框架的使用者选择声明式事物管理。它是最少的影响应用代码的一个选项。
因此是最方便的无侵入轻量级容器的想法。
Spring的AOP使得声明式事物成为了可能。尽管面向方面的事物随着spring发布了,并且可能用于样板文件中,为了有效使用这段代码不必必须去理解aop的概念。
Spring声明式事物的好处
1.Unlike EJB CMT, which is tied to JTA, the Spring Framework's declarative transaction management works in any environment. It can work with JDBC, JDO, Hibernate or other transactions under the covers, with configuration changes only.
  不像EJB的CMT,和JTA联系在一起,Spring的声明事物管理可以工作在任何环境里。在幕后他可以与JDBC,JDO,Hibernate或是其他事物工作在一起。并且只需要修改配置文件。
The Spring Framework enables declarative transaction management to be applied to any class, not merely special classes such as EJBs.
Spring框架能使声明事物管理任何类,不仅仅是像EJB的特殊类
The Spring Framework offers declarative rollback rules: this is a feature with no EJB equivalent. Both programmatic and declarative support for rollback rules is provided.
  Spring框架提供声明式事物回滚规则,这是一个EJB不具有的特点。编程式事物和声明式事物都支持回滚规则
The Spring Framework gives you an opportunity to customize transactional behavior, using AOP. For example, if you want to insert custom behavior in the case of transaction rollback, you can. You can also add arbitrary advice, along with the transactional advice. With EJB CMT, you have no way to influence the container's transaction management other than setRollbackOnly().
Spring框架给你一个自定义事物行为的机会,使用AOP,例如,如果你想在事物回滚时插入自定义的行为。你也可以随着事物建议插入强制的建议。使用EJB的CMT,你没有方式去影响容器的事物管理。除了使用setRollbackOnly().
The Spring Framework does not support propagation of transaction contexts across remote calls, as do high-end application servers. If you need this feature, we recommend that you use EJB. However, consider carefully before using such a feature, because normally, one does not want transactions to span remote calls.
Spring框架不支持跨远程方法调用的事物上下文传播。作为高端服务器。如果你需要这个特点。我们建议你使用EJB。然而在使用这样一个特点之前你仔细考虑一下。因为通常情况下,不希望事物跨远程方法调用。
Spring声明式事物的实现原理
Spring声明式事物凭借的是AOP 代理,AOP代理使用的是一个TransactionInterceptor 配合PlatformTransactionManager 的实现类去驱动事物相关的方法被调用。
实例:
package x.y.service;

public interface FooService {
  Foo getFoo(String fooName);
  Foo getFoo(String fooName, String barName);
  void insertFoo(Foo foo);
  void updateFoo(Foo foo);
}
如果前两个方法要在事物的读的上下文中进行,后两个要在事物的读、写的上下文中进行。
采用下面的配置上面的问题就可以都得到解决。
<?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:tx="http://www.springframework.org/schema/tx"
     xsi:schemaLocation="
     http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
     http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
     http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
 
  <!-- 这个service对象要做事物的 -->
  <bean id="fooService" class="x.y.service.DefaultFooService"/>

  <!-- 定义发生了什么 -->
<aop:advisor/>
  <tx:advice id="txAdvice" transaction-manager="txManager">//指明事物管理器
  <!-- 事物语义-->
  <tx:attributes>
    <!-- 所有以get开头的方法是只读的-->
    <tx:method name="get*" read-only="true"/>
    <!-- 其他的方法采用默认的事物即读写 -->
    <tx:method name="*"/>
  </tx:attributes>
  </tx:advice>
 
  <!--  -->
  <aop:config>
  <aop:pointcut id="fooServiceOperation" expression="execution(* x.y.service.FooService.*(..))"/>//定义切点,FooService包下的任意一个方法,该方法是任意参数,任意返回任何参数类型的
  <aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/>//链接advice和pointcut是advisor
  </aop:config>
 
    <!--定义DBCP数据源 -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
  <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
  <property name="url" value="jdbc:oracle:thin:@rj-t42:1521:elvis"/>
  <property name="username" value="scott"/>
  <property name="password" value="tiger"/>
  </bean>
   
    <!--使用JDBC数据源,如果使用Hibernate,则下面引用要用sessionFactory -->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource"/>
  </bean>
 
  <!-- other <bean/> definitions here -->
</beans>
注:The transaction semantics that we want to apply are encapsulated in the <tx:advice/> definition
   我们要使用的事物语义被封装在<tx:advice/>定义中。
事物的回滚
Spring事物架构默认情况下当运行时抛出 unchecked exceptions只有标准了rollback才会进行事物的回滚。就是抛出了 一个异常实例或是RuntimeException的之类。 unchecked exceptions从事物方法中抛出将不会导致事物的回滚。
下面是一个基于xml配置的标准事物回滚的实例。
<tx:advice id="txAdvice" transaction-manager="txManager">
  <tx:attributes>
  <tx:method name="get*" read-only="true" rollback-for="NoProductInStockException"/>
  <tx:method name="*"/>
  </tx:attributes>
</tx:advice>
当然还可以指定即使出现异常仍然提交事物
实例如下:
<tx:advice id="txAdvice">
  <tx:attributes>
  <tx:method name="updateStock" no-rollback-for="InstrumentNotFoundException"/>
  <tx:method name="*"/>
  </tx:attributes>
</tx:advice>
出现任何异常都进行事物回滚的实例
<tx:advice id="txAdvice">
  <tx:attributes>
  <tx:method name="*" rollback-for="Throwable"/>
  </tx:attributes>
</tx:advice>
为不同的beans配置不同的事物语义(<aop:advisor/>)
假设你所有服务层的class都放在'x.y.service' package下,所有服务层的class以'Service' 结尾。是所有的服务都有默认的事物可以使用如下的配置:
<?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:tx="http://www.springframework.org/schema/tx"
  xsi:schemaLocation="
  http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
  http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
  http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">

  <aop:config>

    <aop:pointcut id="serviceOperation"
          expression="execution(* x.y.service..*Service.*(..))"/>
    //表示所有x.y.service包下的(包括子包)以Service结尾的任意方法
    <aop:advisor pointcut-ref="serviceOperation" advice-ref="txAdvice"/>

  </aop:config>

  <!-- these two beans will be transactional... -->
  <bean id="fooService" class="x.y.service.DefaultFooService"/>
  <bean id="barService" class="x.y.service.extras.SimpleBarService"/>

  <!-- ... and these two beans won't -->
  <bean id="anotherService" class="org.xyz.SomeService"/> <!-- (not in the right package) -->
  <bean id="barManager" class="x.y.service.SimpleBarManager"/> <!-- (doesn't end in 'Service') -->

  <tx:advice id="txAdvice">
    <tx:attributes>
      <tx:method name="get*" read-only="true"/>
      <tx:method name="*"/>
    </tx:attributes>
  </tx:advice>

  <!-- other transaction infrastructure beans such as a PlatformTransactionManager omitted... -->
</beans>
也可以定义多个advice和advisor如下:
<aop:config>

    <aop:pointcut id="defaultServiceOperation"
          expression="execution(* x.y.service.*Service.*(..))"/>

    <aop:pointcut id="noTxServiceOperation"
          expression="execution(* x.y.service.ddl.DefaultDdlManager.*(..))"/>

    <aop:advisor pointcut-ref="defaultServiceOperation" advice-ref="defaultTxAdvice"/>

    <aop:advisor pointcut-ref="noTxServiceOperation" advice-ref="noTxAdvice"/>

  </aop:config>

  <!-- this bean will be transactional (see the 'defaultServiceOperation' pointcut) -->
  <bean id="fooService" class="x.y.service.DefaultFooService"/>

  <!-- this bean will also be transactional, but with totally different transactional settings -->
  <bean id="anotherFooService" class="x.y.service.ddl.DefaultDdlManager"/>

  <tx:advice id="defaultTxAdvice">
    <tx:attributes>
      <tx:method name="get*" read-only="true"/>
      <tx:method name="*"/>
    </tx:attributes>
  </tx:advice>

  <tx:advice id="noTxAdvice">
    <tx:attributes>
      <tx:method name="*" propagation="NEVER"/>
    </tx:attributes>
  </tx:advice>
默认的 <tx:advice/> settings
The propagation setting is REQUIRED 传播设置是必须的
The isolation level is DEFAULT   隔离级别是默认的
The transaction is read/write    事物是读写的
<tx:method/> 设置
Attribute Required? Default Description
name Yes   The method name(s) with which the transaction attributes are to be associated. The wildcard (*) character can be used to associate the same transaction attribute settings with a number of methods; for example, 'get*', 'handle*', 'on*Event', and so forth.

propagation No REQUIRED The transaction propagation behavior
isolation No DEFAULT The transaction isolation level
timeout No -1 The transaction timeout value (in seconds)
read-only No false Is this transaction read-only?
rollback-for No   The Exception(s) that will trigger rollback; comma-delimited. For example, 'com.foo.MyBusinessException,ServletException'

no-rollback-for No   The Exception(s) that will not trigger rollback; comma-delimited. For example, 'com.foo.MyBusinessException,ServletException'
tx:method的name是必须的,如指定get*开头的。
propagation属性默认是required。
isolation 事物的隔离级别默认是default
read-only 属性也是默认是false
rollback-for 指定出现哪些异常要进行事物的回滚

下面使用@Transactional事物
这个功能必须是在java5或是之上版本才可用
实例:
@Transactional
public class DefaultFooService implements FooService {
  Foo getFoo(String fooName);
  Foo getFoo(String fooName, String barName);
  void insertFoo(Foo foo);
  void updateFoo(Foo foo);
}
当上面的pojo被Spring容器管理,只要在配置文件中添加一行就可以是上面的服务受事物的管理。配置文件如下:
<?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:tx="http://www.springframework.org/schema/tx"
     xsi:schemaLocation="
     http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
     http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
     http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
 
  <!-- this is the service object that we want to make transactional -->
  <bean id="fooService" class="x.y.service.DefaultFooService"/>

  <!-- enable the configuration of transactional behavior based on annotations -->
  <tx:annotation-driven transaction-manager="txManager"/>
  //上面的这一行就表示声明式的事物管理了
  <!-- a PlatformTransactionManager is still required -->
  <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <!-- (this dependency is defined somewhere else) -->
  <property name="dataSource" ref="dataSource"/>
  </bean>
  //事物管理器只需要直接引入数据源即可
  <!-- other <bean/> definitions here -->
</beans>
@Transactional可以被使用在接口上,接口的方法上,一个class上,或是一个class的方法上。
Spring的开发团队建议@Transactional被用到实体bean上,反对作用在接口上,如果你作用在接口上,只有你使用interface-based proxies(基于接口的代理)才会工作,如果使用class-based proxies (proxy-target-class="true") or the weaving-based aspect (mode="aspectj"),被设置的类将不会生效事物的管理。
模式使用的是代理模式,这种代理模式是对外方法进行链接的。内部的对象调用另一个对象的方法将不会被拦截。
<tx:annotation-driven/> settings
attribute              default
transaction-manager    transactionManager
mode                   proxy
proxy-target-class     false
注意:<tx:annotation-driven/>只会寻找同一个应用上下文锁定义有@Transactional 的beans。
如果你把 <tx:annotation-driven/>放到DispatcherServlet对应的ApplicationContext,就只会寻找controller里面的@Transactional 的beans。而不是你的service。
在方法上设置@Transactional会优先与类上设置@Transactional
如:
@Transactional(readOnly = true)
public class DefaultFooService implements FooService {

  public Foo getFoo(String fooName) {
    // do something
  }

  // these settings have precedence for this method
  @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
  public void updateFoo(Foo foo) {
    // do something
  }
}
上面的方法中getFoo执行类定义的事物,而updateFoo执行自己定义的事物方式。
@Transactional settings
The propagation setting is PROPAGATION_REQUIRED
The isolation level is ISOLATION_DEFAULT
The transaction is read/write
The transaction timeout defaults to the default timeout of the underlying transaction system, or or none if timeouts are not supported
Any RuntimeException will trigger rollback, and any checked Exception will not
propagation  enum: Propagation optional propagation setting
isolation  enum: Isolation optional isolation level
readOnly  boolean read/write vs. read-only transaction
timeout  int (in seconds granularity) the transaction timeout
rollbackFor  an array of Class objects, which must be derived from Throwable an optional array of exception classes which must cause rollback
rollbackForClassname  an array of class names. Classes must be derived from Throwable an optional array of names of exception classes that must cause rollback
noRollbackFor  an array of Class objects, which must be derived from Throwable an optional array of exception classes that must not cause rollback.
noRollbackForClassname 
PROPAGATION_REQUIRED表示该线程在事物的中运行
待续......
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics