说明:未整理版,未完待续,请绕行

本部分的重点是数据访问以及数据访问层与业务层之间的交互。

1、Spring框架的事务管理 介绍

http://docs.spring.io/spring/docs/current/spring-framework-reference/html/transaction.html

为什么要使用Spring事务管理?最有竞争力的原因是综合的事务支持!

Spring框架提供了一致的事务管理抽象,能够带来如下好处:

  1. 不同事务APIs, 相同的编程模型。不同的事务APIs:例如Java Transaction API (JTA)、JDBC、Hibernate、Java Persistence API (JPA)、以及Java Data Objects (JDO)。
  2. 支持声明式事务管理。
  3. 针对编码式事务管理的简化API -- 相对于复杂事务APIs来说,如JTA。
  4. 与Spring的数据访问抽象的高度集成。

下面的部分描述了Spring框架的事务增值(value-adds)和技术。(该章也包含了最佳实践、应用服务器集成 以及常见文件的解决方案的讨论。)

2、Spring框架事务支持模型的领先之处

传统上,Java EE 开发者有两个选择:全局的 或者 局部的 事务管理,二者都有其限制。

2.1、全局事务

全局事务可以让你使用跨越多事务的资源进行工作,典型的是关系型数据库和消息队列。应用服务器通过JTA管理全局事务。JTA 是一个臃肿的API(部分是因为它的异常模型)。更多的是,一个JTA UserTransaction 通常需要同JNDI种获取源,就是说你还需要使用JNDI。

显然,使用全部事务会限制应用代码的任何复用,因为JTA通常只在应用服务器环境中可用。

从前,推荐的方法是通过EJB CMT (Container Managed Transaction) 来使用全局事务,CMT是一种声明式事务管理。EJB CMT移除了事务有关的JNDI查找,虽然其自身对EJB的使用导致必须依赖JNDI。它移除了多数但不是全部对于Java代码的需求--以控制事务。最明显的负面是CMT被绑定到了JTA和应用服务器环境。EJB的负面太明显,所以不够吸引人。

2.2、本地事务

本地事务是资源专有的,例如关联到JDBC连接的事务。本地事务可能更容易使用,但有明显的缺点:它们可能无法在跨越多事务的资源中使用。例如,管理使用JDBC连接的事务的代码不能运行在一个全局JTA事务中。因为应用服务器没有被卷入事务管理,它无法帮助确认跨越多个资源的正确性。(必须指出,多数应用使用都在使用一个单独的事务资源)。另一个负面是本地事务对于编程模型来说是入侵性的。

2.3、Spring框架的一致的编程模型

Spring解决了全局的和本地的事务的缺点。它使得应用开发者们在任何环境中都可以使用一致的编程模型。一次编码,可以在不同的环境中从不同的事务管理策略受益。Spring框架提供了声明式和编码式的事务管理。多数用户倾向于声明式事务管理--多数情节下也是我们推荐的。

使用编码式事务管理时,开发者们使用Spring框架的事务抽象 可以运行在任何底层的事务设施之上。使用声明式事务管理,开发者们通常只编写少量甚至没有的事务代码,所以不需要依赖于Spring框架的事务API,或者任何其他事务API。

你需要一个事务管理的应用服务器吗?

Spring框架的事务管理支持改变传统的规则 -- 一个企业级Java应用需要一个应用服务器。

特别的是,你不需要为EJB的声明式事务准备一个应用服务器。事实上,即使你的应用服务器拥有强大的JTA能力,你仍然可能认为Spring框架的声明式事务提供了更强大和更具备生产力的编程模型。

典型的,只有你的应用需要处理跨越多个资源的事务时(很多应用没有这样的需求),你才需要一个应用服务器的JTA能力。许多高端应用使用一个单一的、高度可伸缩的数据库(例如Oracle RAC)来代替。独立的事务管理器如Atomikos Transactions 和 JOTM 是别的选择。当然,你可能需要其他应用服务器能力,例如JMS和Java EE Connector Architecture(JCA)。

3、理解Spring框架的事务抽象

通往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,虽然它可被编码使用。因为PlatformTransactionManager是一个接口,它可以很简单的被mocked或stubbed。它没有绑到一个查找策略(例如JNDI)。PlatformTransactionManager实现如同Spring框架的IoC容器中的所有其他对象(或bean)一样被定义。这使得Spring框架事务成为一个很好的抽象,即便你使用JTA。事务代码可以很简单的被测试。

Spring的哲学,PlatformTransactionManager接口的任意方法抛出的TransactionException都是unchecked(就是说,继承了java.lang.RuntimeException)。

getTransaction(..)方法返回一个TransactionStatus对象,依赖于一个TransactionDefinition参数。返回的TransactionStatus可能代表一个新的事务,或者可能代表一个已有的事务--如果匹配的事务存在于当前的call stack中。后一种情况,当在Java EE 事务上下文中时,一个TransactionStatus是关联到执行的一个线程。

TransactionDefinition接口指出了:

Isolation : 事务与其他事务的工作相隔离的度量。例如,这个事务能否看见其他事务的未提交的写入?

Propagation : 典型的情况是,在一个事务范围内执行的所有代码都会在该事务内执行。【感觉是废话?】然而,。。。 例如,代码可以继续运行在现有的事务中(通常情况);或者,现有事务可以被挂起,一个新的事务被创建。Spring提供了类似EJB CMT的所有事务propagation选项。后面有说。

Timeout : 一个事务可以在超时和自动回滚前运行多久。

Read-only status : 当你的代码只读取而不修改数据时,可以使用只读的事务。

这些设置折射了标准事务的概念。如果有必要,查阅事务隔离级别和其他事务概念的相关资料。理解这些概念是使用Spring框架或任何事务管理器解决方案的基础。

TransactionStatus接口提供了一个简单的方式以编码控制事务的执行和查询事务状态。

public interface TransactionStatus extends SavepointManager {

    boolean isNewTransaction();

    boolean hasSavepoint();

    void setRollbackOnly();

    boolean isRollbackOnly();

    void flush();

    boolean isCompleted();

}

在Spring中,无论你倾向于声明式还是编码式事务管理,定义正确的PlatformTransactionManager实现是绝对必须的。一般,你可以通过依赖注入来定义该实现。

PlatformTransactionManager实现通常要求其工作的环境的知识,如JDBC、JTA、Hibernate以及其他。见下例(简单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>

如果你在一个Java EE容器中使用JTA,请使用一个容器DataSource -- 通过JNDI获取。看起来像这样:

<?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.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee.xsd"> <jee:jndi-lookup id="dataSource" jndi-name="jdbc/jpetstore"/> <bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager" /> <!-- other <bean/> definitions here --> </beans>

上面,JtaTransactionManager不必知道DataSource,或者其他特定的资源,因为它使用容器的全局事务管理设施。

另外,以后会讲到<jee:jndi-lookup/>。

你也可以使用Hibernate的本地事务,很简单,见下面的例子。下面的例子,只需要定义一个Hibernate的LocalSessionFactoryBean,用于获取Hibernate session对象。

另外,DataSource的定义类似于上面的本地JDBC例子,略。

If the DataSource, used by any non-JTA transaction manager, is looked up via JNDI and managed by a Java EE container, then it should be non-transactional because the Spring Framework, rather than the Java EE container, will manage the transactions.

<bean id="sessionFactory" class="org.springframework.orm.hibernate5.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.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>

如果使用Hibernate和Java EE 容器管理的JTA事务,应该和前面的(使用JDBC的JTA)例子使用相同的JtaTransactionManager:

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

所有的cases,应用代码都不需要改变!仅仅修改配置即可。

4、使用事务同步资源

本部分描述了应用代码如何直接或间接的使用持久层API如JDBC、Hibernate、或者JDO,来确保资源被正确的创建、复用已经清理。还讨论了事务同步是怎么通过相关的PlatformTransactionManager引发的 (可选)?

4.1、高级别同步

推荐使用Spring的基于持久层集成APIs的最高级模板,或者使用transaction-aware的工厂bean或管理native资源工厂的代理 的 native ORM APIs。 -- 真尼玛绕!!!

这些transaction-aware的解决方案内部处理资源的创建和复用以及清理、资源的可选的事务同步和异常应用。因此,用户数据访问代码不需要做这些任务,集中于单纯的逻辑即可。一般,使用native ORM API或者使用JdbcTemplate来处理JDBC访问。后面详述。

4.2、低级别同步

诸如DataSourceUtils(JDBC)、EntityManagerFactoryUtils(JPA)、SessionFactoryUtils(Hibernate)、PersistenceManagerFactoryUtils(JDO)此类的类,都是低级别的。当你想使用代码直接处理native persistence APIs 的资源类型时,可以使用这些类来确保获取合适的Spring框架管理的实例、同步事务(可选)、以及正确的映射异常到相关的API中。

例如,JDBC时,你可以使用org.springframework.jdbc.datasource.DataSourceUtils:

Connection conn = DataSourceUtils.getConnection(dataSource);

如果现有的事务已经有一个连接同步连接到它,那个实例会被返回。否则,该方法会创建一个新的连接--可能同步连接到任何已有的事务,让后续的复用可以用于相同的事务。SQLException是被封装在Spring框架的CannotGetJdbcConnectionException中,unchecked。会给出更多信息。

没有Spring事务管理时也能工作--就是说事务管理是可选的!

当然,一旦你使用了Spring的JDBC支持、JPA支持或者Hibernate支持,你会倾向于不使用DataSourceUtils或者其他帮助类,因为使用Spring抽象更愉悦。例如,如果使用Spring JdbcTemplate 或者 jdbc.object包来简化JDBC的使用,不必纠结于连接的处理。

4.3、TransactionAwareDataSourceProxy

最低级别!!!绝大多数情况下不建议使用它。

5、声明式事务管理

Spring框架的声明式事务管理使用Spring AOP实现,但你不需要理解AOP也能使用。

Spring不支持跨越远程调用的事务上下文的propagation。如果需要该功能,推荐使用EJB。

Spring 2.0及以后的版本,不再需要配置 TransactionProxyFactoryBean beans。--之前的仍适用,不推荐。

rollback rules的概念非常重要:它们使得你可以指定什么异常应该导致自动回滚。你在配置中声明式的指定,而非在Java code中。所以,虽然你仍然可以调用TransactionStatus对象的setRollbackOnly()方法来回滚当前事务,多数时候,你可以指定一个规则:MyApplicationException必须导致一个回滚。这样做最大的优势在于业务对象不会依赖于事务设施。例如,不需要导入Spring 事务的APIs或者其他Spring的APIs。

虽然EJB容器默认行为是遇到system exception (通常是runtime exception)就自动回滚事务,EJB CMT不会因为application exception(checked exception other than java.rmi.RemoteException)而回滚。Spring声明式事务管理的默认行为同EJB的管理,但可以定制。

5.1、理解Spring框架的声明式事务实现

如果只告诉你使用@Transactional来注解你的类,在你的配置中添加@EnableTransactionManagement,然后期望你理解它的全部工作,这样是远远不够的。这一部分解释了在事务相关问题的事件中Spring框架的声明式事务设施的内部工作。

这里最重要的概念是Spring框架声明式事务支持是通过AOP代理开启的!!事务的advice是通过元数据(基于XML或注解)驱动的。AOP和事务的元数据结合起来形成了AOP代理--该代理使用TransactionInterceptor配合PlatformTransactionManager实现来驱动围绕方法调用的事务!!

概念上,调用事务代理上的方法看起来是这样的:

5.2、声明式事务实现的例子

考虑下下面的接口和其实现。这个例子使用Foo和Bar类作为占位符,你可以将精力集中在事务的使用上,而不必关注某一个domain model。DefaultFooService类的每个方法都抛出了UnsupportedOperationException实例,这能让你观察到相应情况下事务的创建与回滚。

// 想使用事务的service接口

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

}
// 上面接口的实现

package x.y.service;

public class DefaultFooService implements FooService {

    public Foo getFoo(String fooName) {
throw new UnsupportedOperationException();
} public Foo getFoo(String fooName, String barName) {
throw new UnsupportedOperationException();
} public void insertFoo(Foo foo) {
throw new UnsupportedOperationException();
} public void updateFoo(Foo foo) {
throw new UnsupportedOperationException();
} }
<!-- 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.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- this is the service object that we want to make transactional -->
<bean id="fooService" class="x.y.service.DefaultFooService"/> <!-- the transactional advice (what 'happens'; see the <aop:advisor/> bean below) -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<!-- the transactional semantics... -->
<tx:attributes>
<!-- all methods starting with 'get' are read-only -->
<tx:method name="get*" read-only="true"/>
<!-- other methods use the default transaction settings (see below) -->
<tx:method name="*"/>
</tx:attributes>
</tx:advice> <!-- ensure that the above transactional advice runs for any execution
of an operation defined by the FooService interface -->
<aop:config>
<aop:pointcut id="fooServiceOperation" expression="execution(* x.y.service.FooService.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/>
</aop:config> <!-- don't forget the DataSource -->
<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> <!-- similarly, don't forget the PlatformTransactionManager -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean> <!-- other <bean/> definitions here --> </beans>

上面假定了FooService的前两个方法(getFoo(String), getFoo(String, String))都必须在只读事务中执行。另外两个方法必须在读写事务中执行。

上面的配置让fooService bean变成事务性的。

常见的需求是让整个service层变成事务性的。最佳做法如下:

<aop:config>
<aop:pointcut id="fooServiceMethods" expression="execution(* x.y.service.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceMethods"/>
</aop:config>

5.3、回滚声明式事务

上面概述了如何指定类的事务设置,特别是service层的类。本部分将描述如何控制声明式事务的回滚。

推荐的方法是从被执行的代码中抛出一个Exception。Spring框架的事务设施会catch所有unhandled Exception,并决定是否让事务回滚。

默认设置下,Spring框架的事务设施只会回滚runtime、unchecked exceptions,就是说抛出的异常是RuntimeException的子类。(默认,Error也会导致回滚)。

不过,你可以配置具体什么Exception类型会导致回滚,可以包括checked exceptions。下面的XML片段演示了如何配置让一个checked、application-specific Exception类型导致回滚。

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

甚至可以指定’no rollback rules’ -- 在什么情况不会回滚!如下:

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

当Spring框架的事务设施catch了一个异常,它会咨询配置中的回滚规则以决定是否将该事务标记为回滚,the stronges matching rule wins -- 最符合的匹配规则起作用。下面的配置中,除了InstrumentNotFoundException都会导致回滚!

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

还可以通过编码标记一个回滚!但,虽然简单,但是侵入式的,会将你的代码与Spring框架的事务设施紧密耦合在一起!如下:

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

强烈建议使用声明式事务来控制回滚,除非不得已,否则不要使用编码式回滚。

5.4、为不同的beans配置不同的事务性语义

思考下这种情景:你有大量的service层对象,你还想为每个对象应用一个完全不同的事务性配置。你可以定义不同的<aop:advisor/>--带有不同的pointcut和advice-ref属性值。

作为对比,先假定所有的service层的类都定义在x.y.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.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.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" 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>

下面的例子示意了如何为不同的beans配置不同的事务:

<?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.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.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" 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> <!-- other transaction infrastructure beans such as a PlatformTransactionManager omitted... --> </beans>

5.5、<tx:advice/>设置

本部分总结了不同的事务性设置 -- 可以通过<tx:advice/>标签指定。

<tx:advice/>默认的设置是:

  • Propagation设置是REQUIRED。
  • Isolation级别是DEFAULT。
  • Transaction是read/write。
  • 事务超时默认是底层事务系统的默认超时,或者没有超时--如果不支持的话。
  • 所有的RuntimeException都会导致回滚,所有的checked Exception不会。

你可以修改这些默认设置。

<tx:advice/>和<tx:attributes/>内部嵌套的<tx:method/>标签的不同属性可以总结为以下内容:

属性 是否必须 默认 描述
name   如:get*, handle*, on*Event等等。
propagation REQUIRED 事务传播行为。
isolation DEFAULT 事务隔离级别。
timeout -1 事务超时时间(秒)。
read-only false 事务是否只读?
rollback-for   导致回滚的异常,多个事务以逗号拼接。
no-rollback-for   不会导致回滚的异常,多个异常时以逗号拼接。

5.6、使用@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);
}

然后只需要这样配置下就可以启用了:

<!-- 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.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.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>

如果PlatformTransactionManager的bean name是transactionManager,那可以省略<tx:annotation/>的transaction-manager属性!否则就需要指定。

@Configuration class中,使用@EnableTransactionManagement具有同样的效果。

当使用代理时,你应该仅在public方法上使用@Transactional注解。虽然private/protected方法上也不会报错,但没有任何效果。

可以在接口、接口的方法、类、类的public方法上使用@Transactional注解。然而,仅仅使用@Transactional注解是不够的,它只是简单的元数据,需要被运行时设施如@Transactional-aware等使用。在前面的例子中,<tx:annotation-driven/>开启了事务性行为。

Spring推荐你只注解在类或其public方法上,而不要注解到接口上。否则,当你你开启了proxy-target-class=”true”或mode=”aspectj”时,会无法识别那些!!!

在proxy mode (默认就是)下,只有外部的方法调用才会被拦截!就是说,类内部方法之间的调用不会开启事务!!!

proxy-target-class属性用来控制被@Transactional注解过的类创建什么类型的事务代理。如果true,则基于class的代理会被创建。如果false或者留空,标准JDK的基于接口的代理会被创建!--详见Spring AOP中的代理机制。--要时刻记住,事务本质上是通过AOP实现的!

@EnableTransactionManagement和<tx:annotation-driven/>只会查找相同应用上下文中的@Transactional beasn!!!就是说,如果将注解驱动的配置放到一个DispatcherServlet的WebApplicationContext中,只会检查你的controllers当中的@Transactional beans,而不会检查services。

如果类上和类的方法上都有@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设置

默认设置:

  • Propagation设置是PROPAGATION_REQUIRED。
  • Isolation级别是ISOLATION_DEFAULT。
  • Transaction是read/write。
  • Transanction timeout默认是底层事务系统的默认超时时间,或者没有--如果不支持的话。
  • 所有RuntimeException都会导致回滚,所有checked Exception都不会导致回滚。

默认设置可以被改变,见下面@Transactional注解的properties:

属性 类型 描述
value String 可选项,指定使用的事务管理器。
propagation enum:Propagation 可选项,传播行为设置。
Isolation enum:Isolation 可选项,隔离级别。
readOnly boolean read/write vs read-only
timeout int 单位:秒。
rollbackFor Class<? extends Throwable)[] 可选项。
rollbackForClassName   可选项。
noRollbackFor   可选项。
noRollbackForClassName   可选项。

@Transactional与 多个事务管理器

多数Spring应用只需要一个事务管理器,但仍有需要多个事务管理器的情况。@Transactional注解的value属性,可以用来指定需要使用的PlatformTransactionManager。既可以是事务管理器bean 的 bean name也可以是其qualifier。例如:

public class TransactionalService {

    @Transactional("order")
public void setSomething(String name) { ... } @Transactional("account")
public void doSomething() { ... }
}
<tx:annotation-driven/>

    <bean id="transactionManager1" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
...
<qualifier value="order"/>
</bean> <bean id="transactionManager2" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
...
<qualifier value="account"/>
</bean>

如果没有找到指定的事务管理器bean,那会默认使用bean name是transactionManager的bean。

自定义注解

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Transactional("order")
public @interface OrderTx {
} @Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Transactional("account")
public @interface AccountTx {
}
public class TransactionalService {

    @OrderTx
public void setSomething(String name) { ... } @AccountTx
public void doSomething() { ... }
}

5.7、事务传播 propagation

本部分描述了Spring中事务传播的语义。注意,不是对事务传播的介绍,而是Spring中事务传播的一些详细信息。

在Spring管理的事务中,要留意physicallogical事务的区别,以及不同的传播设置。

PROPAGATION_REQUIRED时,会为每个适用的方法创建一个logical事务scope。每个这样的logical transaction scope都能独立的决定rollback-only status,外部事务scope逻辑上独立于内部事务scope。当然,在标准的PROPAGATION_REQUIRED行为里,所有scopes都会被映射到相同的物理事务。所以,内部事务scope中的回滚标记也会影响外部事务的提交!(--什么鬼???看上面的图片)

然而,当内部scope设置的rollback-only marker,外部事务还没决定回滚时,回滚是非预期的。(什么鬼???)。会抛出一个UnexpectedRollbackException。

PROPAGATION_REQUIRES_NEW,相对于PROPAGATION_REQUIRED来说,为每个涉及到的事务scope都使用了完全独立的事务。所以,底层物理事务是不同的,所以提交或回滚都是彼此独立的,外部事务不会被内部事务的回滚影响。

PROPAGATION_NESTED 使用一个单独的物理事务,带有多个可以回滚至的savepoints。这允许内部事务scope在其范围内回滚,而外部事务仍然可以继续物理事务。该设置被映射到JDBC的savepoints,所以只能在JDBC资源事务时工作。参考Spring的DataSourceTransactionManager。

5.8、Advising transactional operations

Spring 4 官方文档学习(九)数据访问之事务管理的更多相关文章

  1. Spring 4 官方文档学习(十二)View技术

    关键词:view technology.template.template engine.markup.内容较多,按需查用即可. 介绍 Thymeleaf Groovy Markup Template ...

  2. Spring 4 官方文档学习(十一)Web MVC 框架之resolving views 解析视图

    接前面的Spring 4 官方文档学习(十一)Web MVC 框架,那篇太长,故另起一篇. 针对web应用的所有的MVC框架,都会提供一种呈现views的方式.Spring提供了view resolv ...

  3. Spring 4 官方文档学习(十一)Web MVC 框架

    介绍Spring Web MVC 框架 Spring Web MVC的特性 其他MVC实现的可插拔性 DispatcherServlet 在WebApplicationContext中的特殊的bean ...

  4. Spring Boot 官方文档学习(一)入门及使用

    个人说明:本文内容都是从为知笔记上复制过来的,样式难免走样,以后再修改吧.另外,本文可以看作官方文档的选择性的翻译(大部分),以及个人使用经验及问题. 其他说明:如果对Spring Boot没有概念, ...

  5. Spring boot官方文档学习(一)

    个人说明:本文内容都是从为知笔记上复制过来的,样式难免走样,以后再修改吧.另外,本文可以看作官方文档的选择性的翻译(大部分),以及个人使用经验及问题. 其他说明:如果对Spring Boot没有概念, ...

  6. Spring 4 官方文档学习(十一)Web MVC 框架之配置Spring MVC

    内容列表: 启用MVC Java config 或 MVC XML namespace 修改已提供的配置 类型转换和格式化 校验 拦截器 内容协商 View Controllers View Reso ...

  7. Spring Framework 官方文档学习(四)之Validation、Data Binding、Type Conversion(一)

    题外话:本篇是对之前那篇的重排版.并拆分成两篇,免得没了看的兴趣. 前言 在Spring Framework官方文档中,这三者是放到一起讲的,但没有解释为什么放到一起.大概是默认了读者都是有相关经验的 ...

  8. Spring Framework 官方文档学习(四)之Validation、Data Binding、Type Conversion(二)

    接前一篇 Spring Framework 官方文档学习(四)之Validation.Data Binding.Type Conversion(一) 本篇主要内容:Spring Type Conver ...

  9. Spring Framework 官方文档学习(四)之Validation、Data Binding、Type Conversion

    本篇太乱,请移步: Spring Framework 官方文档学习(四)之Validation.Data Binding.Type Conversion(一) 写了删删了写,反复几次,对自己的描述很不 ...

随机推荐

  1. 【驱动笔记10】再谈IRP

    文章作者:grayfox作者主页:http://nokyo.blogbus.com原始出处:http://www.blogbus.com/nokyo-logs/34010655.html 这一节会对I ...

  2. Note for video Machine Learning and Data Mining——training vs Testing

    Here is the note for lecture five. There will be several points  1. Training and Testing  Both of th ...

  3. 为 Blade 模板引擎添加新文件扩展名

    因为一些原因,我准备把 Blessing Skin 的框架换成 Laravel 了(之前是自己搭建的一个框架),但是在模板迁移的时候遇到了一点问题. 之前我是使用的 XiaoLer/blade 这个从 ...

  4. libgdx 1.4.1公布

    (转载自http://www.libgdx.cn/topic/4/libgdx-1-4-1%E5%8F%91%E5%B8%83) libgdx从未停止进步的脚步.10月10日.libgdx1.4.1公 ...

  5. Common Probability Distributions

    Common Probability Distributions Probability Distribution A probability distribution describes the p ...

  6. apache配置,禁止指定后缀访问

    每天都有人来服务器上扫描点什么,想下载点数据库或者什么的 服务器是java的,没有asp或者mdb这样的访问 用一下配置,可以禁止asp或者mdb访问,也可以加上zip和rar禁止 <Direc ...

  7. redis基础之python连接redis(五)

    前言 前面介绍了在数据库命令行直接操作redis,现在学习使用python的redis包来操作redis,本人安装的是redis==2.10.6: 系列文章 redis安装和配置 redis命令行操作 ...

  8. pause和resume

    CCSet *m_pPausedTargets;类的成员变量 void CCNode::schedule(SEL_SCHEDULE selector, float interval, unsigned ...

  9. angular学习笔记(二)-创建angular模块

    如果在页面的html标签(或任意标签)中添加ng-app,表示对整个页面应用angular来管理. 他是一个模块. 模块有助于把东西从全局命名空间中隔离. 今天学习如何自定义创建模块: <!DO ...

  10. Python 2.7.9 Demo - 019.01.CRUD oracle by cx_Oracle

    select #coding=utf-8 #!/usr/bin/python import cx_Oracle; conn = None; cursor = None; try: conn = cx_ ...