首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

事物治理

2012-08-25 
事物管理关键抽象Spring事务抽象的关键是事务策略的概念。这个概念由org.springframework.transaction.Plat

事物管理
关键抽象

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;
}

事物的设置由
TransactionDefinition接口指定:
事务隔离:当前事务和其它事务的隔离的程度。例如,这个事务能否看到其他事务未提交的写数据?

事务传播:通常在一个事务中执行的所有代码都会在这个事务中运行。但是,如果一个事务上下文已经存在,有几个选项可以指定一个事务性方法的执行行为:例如,简单地在现有的事务中继续运行(大多数情况);或者挂起现有事务,创建一个新的事务。Spring提供EJB CMT中常见的事务传播选项。

事务超时: 事务在超时前能运行多久(自动被底层的事务基础设施回滚)。

只读状态: 只读事务不修改任何数据。只读事务在某些情况下(例如当使用Hibernate时),是一种非常有用的优化。

TransactionStatus 接口为处理事务的代码提供一个简单的控制事务执行和查询事务状态的方法。

public interface TransactionStatus {

    boolean isNewTransaction();

    void setRollbackOnly();

    boolean isRollbackOnly();
}

使用Spring时,无论你选择编程式还是声明式的事务管理,定义一个正确的 PlatformTransactionManager 实现都是至关重要的。按照Spring的良好风格,这种重要定义都是通过IoC实现的。

一般来说,选择PlatformTransactionManager实现时需要知道当前的工作环境,如JDBC、JTA、Hibernate等。下面的例子来自Spring示例应用——jPetStore——中的dataAccessContext-local.xml文件,其中展示了一个局部PlatformTransactionManager实现是怎么定义的(仅限于纯粹JDBC环境)

我们必须先定义一个JDBC DataSource,然后使用Spring的DataSourceTransactionManager,并传入指向DataSource的引用。

<bean id="dataSource" 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" ref="dataSource"/>
</bean>

如果我们在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" />

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

</beans>

我们也可以很容易地使用Hibernate局部事务,就像下面的Spring框架的 PetClinic 示例应用中的例子一样)。这种情况下,我们需要定义一个Hibernate的 LocalSessionFactoryBean,应用程序从中获取到Hibernate Session 实例。

DataSource 的bean定义同上例类似(这里不再展示)。不过,如果是一个JEE容器提供的 DataSource,它将由JEE容器自身,而不是Spring框架来管理事务。

这种情况中'txManager' bean的类型为 HibernateTransactionManager。同样地,DataSourceTransactionManager 需要一个指向 DataSource 的引用,而 HibernateTransactionManager 需要一个指向 SessionFactory 的引用。

<bean id="sessionFactory" 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" ref="sessionFactory" />
</bean>

我们可以简单地使用 JtaTransactionManager 来处理Hibernate事务和JTA事务,就像我们处理JDBC,或者任何其它的资源策略一样。

<bean id="txManager" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
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/context
           http://www.springframework.org/schema/context/spring-context-2.5.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<context:annotation-config />
<context:component-scan
base-package="com.liyixing.spring.dao, com.liyixing.spring.service" />
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<context:annotation-config>false</context:annotation-config>
<bean id="dataSource" value="com.mysql.jdbc.Driver" />
<property name="url"
value="jdbc:mysql://10.13.166.224:3306/test?useUnicode=false&amp;autoReconnect=true&amp;characterEncoding=utf-8" />
<property name="username" value="root" />
<property name="password" value="" />
</bean>

<bean id="txManager"
ref="dataSource" />
</bean>

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

<aop:config>
<aop:pointcut id="daoTx"
expression="execution(* com.liyixing.spring.dao.imp.*.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="daoTx" />
</aop:config>

<!--
<bean id="myAspect"
transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="true" />
<tx:method name="*" no-rollback-for="RuntimeException" />
</tx:attributes>
</tx:advice>
上面的配置表示,当遇到了RuntimeException并不回滚

<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="true" />
<tx:method name="*" rollback-for="RuntimeException" />
</tx:attributes>
</tx:advice>
上面的配置表示,遇到的RuntimeException回回滚

当Spring框架捕获到一个异常后会检查配置回滚规则来决定是不是要回滚事务,这时候会遵循最匹配的规则。 所以在下面这种配置中,除了InstrumentNotFoundException这种类型的异常不会导致事务回滚以外,其他任何类型的异常都会。

<tx:advice id="txAdvice">
  <tx:attributes>
  <tx:method name="*" rollback-for="Throwable" no-rollback-for="InstrumentNotFoundException"/>
  </tx:attributes>
</tx:advice>

第二种方法是通过 编程式 方式来指定回滚事务。 虽然写法非常的简单,但是这个方法是高侵入性的,并且使你的代码与Spring框架的事务架构高度耦合。 下面的代码片断里示范了Spring框架管理事务的编程式回滚:

public void resolvePosition() {
  try {
    // some business logic...
  } catch (NoProductInStockException ex) {
    // trigger rollback programmatically
    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
  }
}

为不同的bean配置不同的事务语义

现在让我们考虑一下这样的场景,假设你有许多服务对象,你想为他们分别设置 完全不同 的事务语义。 在Spring中,你可以通过分别定义特定的 <aop:advisor/> 元素, 让每个advisor采用不同的 'pointcut' 和 'advice-ref' 属性,来达到目的。

让我们假定你所有的服务层类定义在以 'x.y.service' 为根的包内。 为了让service包(或子包)下所有名字以 '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.*(..))"/>

    <aop:advisor pointcut-ref="serviceOperation" advice-ref="txAdvice"/>

  </aop:config>

  <!-- these two beans will be transactional... -->
  <bean id="fooService" read-only="true"/>
      <tx:method name="*"/>
    </tx:attributes>
  </tx:advice>

  <!-- other transaction infrastructure beans such as a PlatformTransactionManager omitted... -->

</beans>

下面的配置示例演示了两个拥有完全不同的事务配置的bean。

<?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="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" 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>

  <!-- other transaction infrastructure beans such as a PlatformTransactionManager omitted... -->

</beans>

为不同的bean配置不同的事务语义

现在让我们考虑一下这样的场景,假设你有许多服务对象,你想为他们分别设置 完全不同 的事务语义。 在Spring中,你可以通过分别定义特定的 <aop:advisor/> 元素, 让每个advisor采用不同的 'pointcut' 和 'advice-ref' 属性,来达到目的。

让我们假定你所有的服务层类定义在以 'x.y.service' 为根的包内。 为了让service包(或子包)下所有名字以 '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.*(..))"/>

    <aop:advisor pointcut-ref="serviceOperation" advice-ref="txAdvice"/>

  </aop:config>

  <!-- these two beans will be transactional... -->
  <bean id="fooService" read-only="true"/>
      <tx:method name="*"/>
    </tx:attributes>
  </tx:advice>

  <!-- other transaction infrastructure beans such as a PlatformTransactionManager omitted... -->

</beans>

下面的配置示例演示了两个拥有完全不同的事务配置的bean。

<?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="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" 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>

  <!-- other transaction infrastructure beans such as a PlatformTransactionManager omitted... -->

</beans>

<tx:advice/> 有关的设置

这一节里将描述通过 <tx:advice/> 标签来指定不同的事务性设置。默认的 <tx:advice/> 设置如下:

    *

      事务传播设置 是 REQUIRED
    *

      隔离级别是DEFAULT
    *

      事务是 读/写
    *

      事务超时默认是依赖于事务系统的,或者事务超时没有被支持。
    *

      任何 RuntimeException 将触发事务回滚,但是任何 checked Exception 将不触发事务回滚

这些默认的设置当然也是可以被改变的。 <tx:advice/> 和 <tx:attributes/> 标签里的 <tx:method/> 各种属性设置总结如下:

<tx:method/> 有关的设置
1。属性名
2。是否必须
3。默认值
4。描述

name是 与事务属性关联的方法名。通配符(*)可以用来指定一批关联到相同的事务属性的方法。 如:'get*'、'handle*'、'on*Event'等等。
propagation不REQUIRED事务传播行为
isolation不DEFAULT事务隔离级别
timeout不-1事务超时的时间(以秒为单位)
read-only不false事务是否只读?
rollback-for不
将被触发进行回滚的 Exception(s);以逗号分开。 如:'com.foo.MyBusinessException,ServletException'
no-rollback-for不

不 被触发进行回滚的 Exception(s);以逗号分开。 如:'com.foo.MyBusinessException,ServletException'
在写代码的时候,不可能对事务的名字有个很清晰的认识,这里的名字是指会在事务监视器(比如WebLogic的事务管理器)或者日志输出中显示的名字, 对于声明式的事务设置,事务名字总是包含完整包名的类名加上"."和方法名,比如 'com.foo.BusinessService.handlePayment'.

使用 @Transactional

除了基于XML文件的声明式事务配置外,你也可以采用基于注解式的事务配置方法。直接在Java源代码中声明事务语义的做法让事务声明和将受其影响的代码距离更近了,而且一般来说不会有不恰当的耦合的风险,因为,使用事务性的代码几乎总是被部署在事务环境中。

下面的例子很好地演示了 @Transactional 注解的易用性,随后解释其中的细节。先看看其中的类定义:

// the service class that we want to make transactional
@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 IoC容器里时,上述bean实例仅仅通过一 行xml配置就可以使它具有事务性的。如下:

<!-- from the file 'context.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: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" ref="dataSource"/>
  </bean>
 
  <!-- other <bean/> definitions here -->

</beans>

实际上,如果你用 'transactionManager' 来定义 PlatformTransactionManager bean的名字的话,你就可以忽略 <tx:annotation-driven/> 标签里的 'transaction-manager' 属性。 如果 PlatformTransactionManager bean你要通过其它名称来注入的话,你必须用 'transaction-manager' 属性来指定它,如上所示。

@Transactional 注解可以被应用于接口定义和接口方法、类定义和类的 public 方法上。 然而,请注意只是使用 @Transactional 注解并不会启用事务行为, 它仅仅 是一种元数据,能够被可以识别 @Transactional 注解和上述的配置适当的具有事务行为的beans所使用。上面的例子中,其实正是 <tx:annotation-driven/>元素的出现 开启 了事务行为。

Spring团队的建议是你只在具体的类上使用 @Transactional 注解, 而不要注解在接口上。你当然可以在接口(或接口方法)上使用 @Transactional 注解, 但是这只有在你使用基于接口的代理时它才会生效。因为注解是 不能继承 的, 这就意味着如果你正在使用基于类的代理时,事务的设置将不能被基于类的代理所识别,而且对象也不会被事务代理所包装 (这是很糟糕的)。 因此,请接受Spring团队的建议,在具体的类(包括该类的方法)上使用 @Transactional 注解。

注意:在代理模式下(默认的情况),只有从代理传过来的‘外部’方法调用才会被拦截。 这就意味着‘自我调用’是不会触发事务的,比如说,一个在目标对象中调用目标对象其他方法的方法是不会触发一个事务的,即使这个方法被标记为 @Transactional!

如果你期望‘自我调用’被事务覆盖到,可以考虑使用AspectJ 模式(如下所示)。在这种情况下,一开始就没有任何代理的存在; 为了把@Transactional的方法变成运行时的行为,目标类会被‘编织’起来(比如修改它的字节码)。

Table 9.2. <tx:annotation-driven/> 设置
属性默认值描述
transaction-managertransactionManager

使用的事务管理器的名字。只有像在上面的例子那样,事务管理器不是 transactionManager的情况下才需要。
modeproxy

默认的模式“proxy”会用Spring的AOP框架来代理注解过的bean(就像在前面讨论过的那样, 下面代理的语义只对通过代理传递过来的方法调用起效)。 另一种可行的模式"aspectj"会使用Spring的AspectJ事务切面来编织类(通过修改目标对象的字节码应用到任何方法调用上)。 AspectJ织入需要在classpath中有spring-aspects.jar这个文件,并且启用装载时织入 (或者编译时织入). (关于如何设置装载时编织的详情请参见 Section 6.8.4.5, “Spring配置” )
proxy-target-classfalse

只对代理模式有效。决定为那些使用了@Transactional注解的类创建何种事务代理。 如果 "proxy-target-class" 属性被设为 "true", 那么基于类的代理就会被创建。如果 "proxy-target-class"属性被设为"false" 或者没设,那么基于接口的标准JDK代理就会被创建 (关于不同代理类型的解释请参见 Section 6.6, “代理机制” )
orderOrdered.LOWEST_PRECEDENCE

定义事务通知的顺序会作用到使用@Transactional注解的bean上。 更多的关于AOP通知顺序的定义可以在章节(参见 Section 6.2.4.7, “通知顺序”)。 请注意如果不指定任何顺序将会把决定权交给AOP子系统。

在<tx:annotation-driven/>元素上的"proxy-target-class" 属性 控制了有什么类型的事务性代理会为使用@Transactional 来注解的类创建代理。 如果"proxy-target-class" 属性被设为"true",那么基于类的代理就会被创建。 如果"proxy-target-class" 属性被设为"false" 或者没设,那么会创建基于接口的标准JDK代理。

在多数情形下,方法的事务设置将被优先执行。在下列情况下,例如: DefaultFooService 类在类的级别上被注解为只读事务,但是,这个类中的 updateFoo(Foo) 方法的 @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
       
    }
}

@Transactional 有关的设置

@Transactional 注解是用来指定接口、类或方法必须拥有事务语义的元数据。 如:“当一个方法开始调用时就开启一个新的只读事务,并停止掉任何现存的事务”。 默认的 @Transactional 设置如下:

    *

      事务传播设置是 PROPAGATION_REQUIRED
    *

      事务隔离级别是 ISOLATION_DEFAULT
    *

      事务是 读/写
    *

      事务超时默认是依赖于事务系统的,或者事务超时没有被支持。
    *

      任何 RuntimeException 将触发事务回滚,但是任何 checked Exception 将不触发事务回滚

这些默认的设置当然也是可以被改变的。 @Transactional 注解的各种属性设置总结如下:

Table 9.3. @Transactional 注解的属性
属性类型描述
propagation枚举型:Propagation可选的传播性设置
isolation枚举型:Isolation可选的隔离性级别(默认值:ISOLATION_DEFAULT)
readOnly布尔型读写型事务 vs. 只读型事务
timeoutint型(以秒为单位)事务超时
rollbackFor一组 Class 类的实例,必须是Throwable 的子类一组异常类,遇到时 必须 进行回滚。默认情况下checked exceptions不进行回滚,仅unchecked exceptions(即RuntimeException的子类)才进行事务回滚。
rollbackForClassname一组 Class 类的名字,必须是Throwable的子类一组异常类名,遇到时 必须 进行回滚
noRollbackFor一组 Class 类的实例,必须是Throwable 的子类一组异常类,遇到时 必须不 回滚。
noRollbackForClassname一组 Class 类的名字,必须是Throwable 的子类一组异常类,遇到时 必须不 回滚

在写代码的时候,不可能对事务的名字有个很清晰的认识,这里的名字是指会在事务监视器(比如WebLogic的事务管理器)或者日志输出中显示的名字, 对于声明式的事务设置,事务名字总是全限定名+"."+事务通知的类的方法名。比如BusinessService类的handlePayment(..)方法启动了一个事务,事务的名称是:

com.foo.BusinessService.handlePayment

事务传播
当事务传播被设置PROPAGATION_REQUIRED的时候, 会为每一个被应用到的方法创建一个逻辑事务作用域。 每一个这样的逻辑事务作用域都可以自主地决定rollback-only状态,当这样的逻辑事务作用域被外部的一个逻辑事务作用域所包含的时候, 他们在逻辑上是独立的。当然了,对于正常的 PROPAGATION_REQUIRED设置来说,他会被映射到相同的物理事务上。 所以一个标记有rollback-only的内部逻辑事务作用域的确会影响到外部的逻辑事务作用域(就像你所预料的那样)。

然而,当内部的事务作用域标记为rollback-only,同时外部的事务作用域并没有决定要回滚, 这样的回滚是意料不到的(静悄悄地由内部事务作用域触发的): 一个对应的UnexpectedRollbackException 异常会在这个时候被抛出。这是 可以预料到的行为, 只有这样,这个事务的调用者才不会被误导,在事务没有提交的情况下误以为事务已经提交。所以如果内部的事务(外部的调用者并不知情)标记该事务为 rollback-only,而外部的调用者却依旧在不知情的情况下提交后,它需要收到一个 UnexpectedRollbackException 异常来清楚的了解事务并没有提交而是发生了回滚。

<?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:context="http://www.springframework.org/schema/context"
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/context
           http://www.springframework.org/schema/context/spring-context-2.5.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<context:annotation-config />
<context:component-scan
base-package="com.liyixing.spring.dao, com.liyixing.spring.service" />
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<context:annotation-config>false</context:annotation-config>
<bean id="dataSource" value="com.mysql.jdbc.Driver" />
<property name="url"
value="jdbc:mysql://10.13.166.224:3306/test?useUnicode=false&amp;autoReconnect=true&amp;characterEncoding=utf-8" />
<property name="username" value="root" />
<property name="password" value="" />
</bean>

<bean id="txManager"
ref="dataSource" />
</bean>


<tx:annotation-driven mode="proxy"
transaction-manager="txManager" />

<!--
<bean id="myAspect"
org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only


RequiresNew
PROPAGATION_REQUIRES_NEW,与之前相反,为每一个相关的事务作用域使用了一个完全 独立的事务。在这种情况下,物理事务也将是不同的,因此外部事务可以不受内部事务回滚状态的影响独立提交或者回滚。
Nested

PROPAGATION_NESTED 是一个完全不同的设置。它使用了一个单独的物理事务, 这个事务拥有多个可以回滚的保存点。这样部分回滚允许内部事务在它的作用域内触发一个回滚, 并且外部事务能够不受影响的继续。 这通常是对应于JDBC的保存点,所以只会在 JDBC 资源事务管理上起效 (具体请参见 Spring的DataSourceTransactionManager).

结合AspectJ使用 @Transactional

通过AspectJ切面,你也可以在Spring容器之外使用Spring框架的 @Transactional 功能。要使用这项功能你必须先给相应的类和方法加上 @Transactional注解,然后把 spring-aspects.jar 文件中定义的 org.springframework.transaction.aspectj.AnnotationTransactionAspect 切面连接进(织入)你的应用。同样,该切面必须配置一个事务管理器。你当然可以通过Spring框架容器来处理注入.

编程式事务管理

Spring提供两种方式的编程式事务管理:

    *

      使用 TransactionTemplate
    *

      直接使用一个 PlatformTransactionManager 实现

如果你选择编程式事务管理,Spring小组推荐使用 TransactionTemplate。 第二种方法则类似使用JTA的 UserTransaction API (除了异常处理的部分稍微简单点)。

使用TransactionTemplate

TransactionTemplate 采用与Spring中别的 模板 同样的方法, 如 JdbcTemplate 。它使用回调机制,将应用代码从样板式的资源获取和释放代码中解放出来, 这样写出的代码是目的驱动的,把精力集中在开发者想要做的事情上。

热点排行