Spring事务抽象
2016-09-17 17:07
134 查看
摘要: Spring 事务抽象
一致性编程模型跨不同事务APIs,例如,Java事务API(JTA)、JDBC、Hibernate、Java持久化API(JPA)和Java数据对象(JDO)。
支持声明式事务管理。
比复杂事务APIs(例如,JTA)更简单。
完美的与Spring的数据访问抽象集成。
以往,使用全局事务的首选方式是通过EJB CMT(容器管理事务):CMT是声明式事务管理。EJB CMT清除了相关事务的JNDI查找,不过EJB自己需要使用JNDI。它清除大多数(但不是所有)需要编写Java代码控制事务。重要的缺点是,CMT捆绑JTA和应用程序服务器环境。同时,它只有使用EJBs实现业务逻辑,或至少在事务EJB门面中时才有效。
使用编程式事务管理,开发人员使用Spring框架事务抽象,能运行在任意底层事务之上。使用首选的声明式模型,开发人员通常写很少或不写事务管理代码,因此,不依赖于Spring框架事务API,或其它事务API。
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容器中定义的其它对象一样。这个好处让Spring框架事务值得抽象,甚至是使用JTA时。事务代码比直接使用JTA易于测试。
PlatformTransactionManager接口方法抛出的TransactionException是未检测异常(即,继承java.lang.RuntimeException)。事务失败是致命性的。很少情况,应用程序代码能实际从事务失败中恢复,应用程序开发人员能选择捕获并处理TransactionException。优点在于开发人员不必强制这样做。
getTransaction(..)方法依赖于TransactionDefinition参数返回TransactionStatus对象。返回的TransactionStatus可以代表一个新事务,或如果匹配事务在当前调用栈中存在则代表已存在的事务。其实就是后面这种情况,正如Java EE事务上下文,TransactionStatus关联可执行的事务。
TransactionDefinition接口说明:
Isolation(事务隔离级别):事务隔离级别。例如,该事务是否能读取其它的未提交事务?
Propagation(事务传播):通常,所有代码执行在事务范围将运行在事务中。然而,你有一个选项可以指定当事务上下文已存在时事务方法执行的行为。例如,代码能继续运行在已存在的事务中(通常情况);或已存在的事务暂停,创建一个新事务。
事务超时:该事务运行多长时间后失效,并通过底层事务自动回滚。
只读状态:当你的代码读取但不修改数据时,可以使用只读事务。在某些情况下,只读事务有利于优化,例如,当你使用Hibernate时。
这些设置反应标准事务概念。底层这些概念是使用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等等。下面是定义本地PlatformTransactionManager的例子。
定义JDBC 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定义引用DataSource定义:
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
如果你在Java EE容器中使用JTA,那么你可以通过使用JNDI和Spring的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.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,或任意特定资源,因为它使用容器的全局事务管理。
你也能使用Hibernate的本地事务。在这种情况下,你需要定义Hibernate的LocalSessionFactoryBean,你的应用程序代码将使用它获取Hibernate Session实例。
这种情况下,txManager bean是HibernateTransactionManager。正如DataSourceTransactionManager需要引用DataSource一样,HibernateTransactionManager需要引用SessionFactory:
<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事务,那么你只用使用JtaTransactionManager:
<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>
在所有这些情况下,应用程序代码不需要改变。你仅仅需要改变配置来改变如何管理事务。
例如,在JDBC的情况下,传统的JDBC方式在DataSource上调用getConnection()方法,你可以使用Spring的org.springframework.jdbc.datasource.DataSourceUtils:
Connection conn = DataSourceUtils.getConnection(dataSource);
如果已存在的事务已经有一个连接同步(链接)它,实例被返回。否则,方法调用触发器创建新的连接,(可选)同步任意已存在的事务,后续在相同事务中重用。
这种方式不需要Spring事务管理(事务同步是可选的),因此,无论你是否使用Spring管理事务你都能使用它。
当然,一旦你使用Spring的JDBC支持、JPA支持或Hibernate支持,你通常不喜欢使用DataSourceUtils或其它的帮助类。
<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">
<!-- 事务通知-->
<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.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/>
</aop:config>
<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>
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
你能明确配置事务回滚的异常类型。
<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>
<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>
<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>
</beans>
事务传播设置是REQUIRED
事务隔离级别是DEFAULT
事务是读/写
事务超时时间默认为底层事务系统的超时时间,或如果不支持超时就没有
任意RuntimeException异常触发回滚,任意检查Exception不触发
你能改变这些默认设置;嵌套在<tx:advice/>和<tx:attributes/>标签中的<tx:method/>标签的各种属性:
<?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">
<!-- 开启注解配置事务 -->
<tx:annotation-driven transaction-manager="txManager"/>
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- other <bean/> definitions here -->
</beans>
Service声明注解:
@Transactional
public class DefaultFooService implements FooService {
Foo getFoo(String fooName);
Foo getFoo(String fooName, String barName);
void insertFoo(Foo foo);
void updateFoo(Foo foo);
}
注解驱动事务设置选项:
@Transactional设置:
@Transactional的多事务管理器:
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>
自定义简称注解
@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 {
}
当设置事务传播为PROPAGATION_REQUIRED时,为每个方法创建一个逻辑事务范围。每个逻辑事务范围能单独决定只回滚状态,外部事务范围逻辑独立于内部事务范围。当然,在标准的PROPAGATION_REQUIRED情况下,所有这些范围将映射相同的物理事务。因此,只回滚标记设置在内部事务范围不影响外部事务发生的实际提交。
然而,在这种情况下,内部事务范围设置为只回滚标记,外部事务没有决定回滚,因此回滚是意想不到的。相应的UnexpectedRollbackException抛出。
PROPAGATION_REQUIRES_NEW
对比PROPAGATION_REQUIRED,PROPAGATION_REQUIRES_NEW使用完全独立的事务。在这种情况下,底层物理事务是不同的,因此,能独自提交或回滚,外部事务的回滚状态不受内部事务的影响。
PROPAGATION_NESTED
使用一个能回滚到多个保存点的物理事务。这种回滚允许内部事务范围触发它的范围回滚,外部事务能够继续物理事务,尽管一些操作已经回滚。该设置通常映射到JDBC保存点,因此,只能用于JDBC资源事务。
使用TransactionTemplate(推荐)
使用PlatformTransactionManager直接实现
public class SimpleService implements Service {
private final TransactionTemplate transactionTemplate;
public SimpleService(PlatformTransactionManager transactionManager) {
Assert.notNull(transactionManager, "The 'transactionManager' argument must not be null.");
this.transactionTemplate = new TransactionTemplate(transactionManager);
}
public Object someServiceMethod() {
return transactionTemplate.execute(new TransactionCallback() {
public Object doInTransaction(TransactionStatus status) {
updateOperation1();
return resultOfUpdateOperation2();
}
});
}
}
如果没有返回值,则使用TransactionCallbackWithoutResult类:
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(TransactionStatus status) {
updateOperation1();
updateOperation2();
}
});
在业务代码中回滚:
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(TransactionStatus status) {
try {
updateOperation1();
updateOperation2();
} catch (SomeBusinessExeption ex) {
[b]status.setRollbackOnly();[/b]
}
}
});
指定事务设置:
public class SimpleService implements Service {
private final TransactionTemplate transactionTemplate;
public SimpleService(PlatformTransactionManager transactionManager) {
Assert.notNull(transactionManager, "The 'transactionManager' argument must not be null.");
this.transactionTemplate = new TransactionTemplate(transactionManager);
this.transactionTemplate.setIsolationLevel(
TransactionDefinition.ISOLATION_READ_UNCOMMITTED);
this.transactionTemplate.setTimeout(30); // 30秒
}
}
使用Spring XML配置TransactionTemplate:
<bean id="sharedTransactionTemplate"
class="org.springframework.transaction.support.TransactionTemplate">
<property name="isolationLevelName" value="ISOLATION_READ_UNCOMMITTED"/>
<property name="timeout" value="30"/>
</bean>
def.setName("SomeTxName");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = txManager.getTransaction(def);
try {
// 执行业务逻辑
} catch (MyException ex) {
txManager.rollback(status);
throw ex;
}
txManager.commit(status);
@Componentpublic class MyComponent {
@TransactionalEventListener
public void handleOrderCreatedEvent(CreationEvent<Order> creationEvent) {
...
}
}
TransactionalEventListener注解暴露phase属性允许定制监听事务阶段。有效的阶段是:BEFORE_COMMIT、AFTER_COMMIT(默认)、AFTER_ROLLBACK和AFTER_COMPLETION。
1 介绍Spring框架事务管理
全面的事务支持是使用Spring框架令人信服的原因。Spring框架为事务管理提供一致性抽象,拥有以下好处:一致性编程模型跨不同事务APIs,例如,Java事务API(JTA)、JDBC、Hibernate、Java持久化API(JPA)和Java数据对象(JDO)。
支持声明式事务管理。
比复杂事务APIs(例如,JTA)更简单。
完美的与Spring的数据访问抽象集成。
2 Spring框架的事务支持模型的优点
传统上,Java EE开发人员有两种可选的事务管理方式:全局或本地事务,都有各自的局限性。2.1 全局事务
全局事务让你能使用多个事务资源,通常是关系型数据库和消息队列。应用程序通过JTA管理全局事务,这是一个笨重的API。而且,JTA UserTransaction通常需要来自JNDI,意味着你需要使用JNDI。显然,使用全局事务会限制应用程序代码的重用,因为JTA通常只在应用程序服务器环境有效。以往,使用全局事务的首选方式是通过EJB CMT(容器管理事务):CMT是声明式事务管理。EJB CMT清除了相关事务的JNDI查找,不过EJB自己需要使用JNDI。它清除大多数(但不是所有)需要编写Java代码控制事务。重要的缺点是,CMT捆绑JTA和应用程序服务器环境。同时,它只有使用EJBs实现业务逻辑,或至少在事务EJB门面中时才有效。
2.2 本地事务
本地事务时特定资源,例如,事务关联JDBC连接。本地事务易于使用,但有明显缺点:它们不能跨多个事务资源。例如,使用JDBC连接管理事务不能运行在全局JTA事务中。因为应用程序服务器没有负责事务管理,它不保证跨资源的正确性。另一个缺点是本地事务时入侵式编程模型。2.3 Spring框架的一致性编程模型
Spring解决了全局和本地事务的缺点。它让开发人员在任意环境使用一致性编程模型。开发人员只需编写自己的代码,它能抽离不同环境中的不同的事务管理。Spring框架提供声明式和编程式事务管理。大多数用户喜欢声明式事务管理,这也是推荐的。使用编程式事务管理,开发人员使用Spring框架事务抽象,能运行在任意底层事务之上。使用首选的声明式模型,开发人员通常写很少或不写事务管理代码,因此,不依赖于Spring框架事务API,或其它事务API。
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容器中定义的其它对象一样。这个好处让Spring框架事务值得抽象,甚至是使用JTA时。事务代码比直接使用JTA易于测试。
PlatformTransactionManager接口方法抛出的TransactionException是未检测异常(即,继承java.lang.RuntimeException)。事务失败是致命性的。很少情况,应用程序代码能实际从事务失败中恢复,应用程序开发人员能选择捕获并处理TransactionException。优点在于开发人员不必强制这样做。
getTransaction(..)方法依赖于TransactionDefinition参数返回TransactionStatus对象。返回的TransactionStatus可以代表一个新事务,或如果匹配事务在当前调用栈中存在则代表已存在的事务。其实就是后面这种情况,正如Java EE事务上下文,TransactionStatus关联可执行的事务。
TransactionDefinition接口说明:
Isolation(事务隔离级别):事务隔离级别。例如,该事务是否能读取其它的未提交事务?
Propagation(事务传播):通常,所有代码执行在事务范围将运行在事务中。然而,你有一个选项可以指定当事务上下文已存在时事务方法执行的行为。例如,代码能继续运行在已存在的事务中(通常情况);或已存在的事务暂停,创建一个新事务。
事务超时:该事务运行多长时间后失效,并通过底层事务自动回滚。
只读状态:当你的代码读取但不修改数据时,可以使用只读事务。在某些情况下,只读事务有利于优化,例如,当你使用Hibernate时。
这些设置反应标准事务概念。底层这些概念是使用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等等。下面是定义本地PlatformTransactionManager的例子。
定义JDBC 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定义引用DataSource定义:
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
如果你在Java EE容器中使用JTA,那么你可以通过使用JNDI和Spring的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.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,或任意特定资源,因为它使用容器的全局事务管理。
你也能使用Hibernate的本地事务。在这种情况下,你需要定义Hibernate的LocalSessionFactoryBean,你的应用程序代码将使用它获取Hibernate Session实例。
这种情况下,txManager bean是HibernateTransactionManager。正如DataSourceTransactionManager需要引用DataSource一样,HibernateTransactionManager需要引用SessionFactory:
<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事务,那么你只用使用JtaTransactionManager:
<bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>
在所有这些情况下,应用程序代码不需要改变。你仅仅需要改变配置来改变如何管理事务。
4 使用事务同步资源
现在应该清楚如何创建不同的事务管理器,和它们如何链接需要同步事务的相关资源(例如,DataSourceTransactionManager链接DataSource,HibernateTransactionManager链接SessionFactory等等)。4.1 高级同步方式
首选方式是使用Spring的高级模板基于持久化集成APIs或使用带有事务的本地ORM APIs——感知工厂bean或代理管理本地资源工厂。这些事务感知解决方案内部处理资源创建、重用、清理、资源事务同步选项和异常映射。因此,数据访问代码没有处理这些任务,但可以关注非样板式持久化逻辑。通常,你使用本地ORM API或通过使用JdbcTemplate获取模板。4.2 低级同步方式
例如,DataSourceUtils(JDBC)、EntityManagerFactoryUtils(JPA)、SessionFactoryUtils(Hibernate)、PersistenceManagerFactoryUtils(JDO)存在底层同步方式。当你想应用程序代码直接处理本地持久化APIs,你使用这些类确保获取Spring框架管理的实例,事务是(可选)同步的,发生在进程中的异常正确映射一致性API。例如,在JDBC的情况下,传统的JDBC方式在DataSource上调用getConnection()方法,你可以使用Spring的org.springframework.jdbc.datasource.DataSourceUtils:
Connection conn = DataSourceUtils.getConnection(dataSource);
如果已存在的事务已经有一个连接同步(链接)它,实例被返回。否则,方法调用触发器创建新的连接,(可选)同步任意已存在的事务,后续在相同事务中重用。
这种方式不需要Spring事务管理(事务同步是可选的),因此,无论你是否使用Spring管理事务你都能使用它。
当然,一旦你使用Spring的JDBC支持、JPA支持或Hibernate支持,你通常不喜欢使用DataSourceUtils或其它的帮助类。
4.3 TransactionAwareDataSourceProxy
在底层,已经存在TransactionAwareDataSourceProxy类。这是目标DataSource代理,包装目标DataSource到感知Spring管理事务。为此,它类似于Java EE服务器提供的传统JNDI DataSource。5 声明式事务管理
Spring框架的声明式事务管理让Spring面向切面编程(AOP)成为可能,尽管,Spring框架发布包以模板形式自带事务切面代码,一般需要理解AOP。5.1 理解Spring框架的声明式事务实现
通过元数据驱动事务通知(当前基于XML或注解)。AOP联合事务元数据产生AOP代理,使用TransactionInterceptor联合适当的PlatformTransactionManager实现驱动事务环绕方法调用。5.2 声明式事务实现的例子
<?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">
<!-- 事务通知-->
<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.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/>
</aop:config>
<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>
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
5.3 回滚声明式事务
默认情况下,只有抛出运行时异常或Error时导致Spring事务回滚。检查异常不导致Spring事务回滚。你能明确配置事务回滚的异常类型。
<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>
5.4 为不同的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.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>
<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>
</beans>
5.5 <tx:advice/>设置
默认设置:事务传播设置是REQUIRED
事务隔离级别是DEFAULT
事务是读/写
事务超时时间默认为底层事务系统的超时时间,或如果不支持超时就没有
任意RuntimeException异常触发回滚,任意检查Exception不触发
你能改变这些默认设置;嵌套在<tx:advice/>和<tx:attributes/>标签中的<tx:method/>标签的各种属性:
属性 | 必须? | 默认值 | 描述 |
name | Y | 事务属性关联的方法名。通配符(*)匹配任意数量的字符。 | |
propagation | N | REQUIRED | 事务传播行为。 |
isolation | N | DEFAULT | 事务隔离级别。 |
timeout | N | -1 | 事务超时值(秒)。 |
N | |||
read-only | N | false | 是否是只读事务。 |
rollback-for | N | 触发回滚的异常;逗号分隔。 | |
no-rollback-for | N | 不触发回滚的异常;逗号分隔。 |
5.6 使用@Transactional
开启注解管理事务:<?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">
<!-- 开启注解配置事务 -->
<tx:annotation-driven transaction-manager="txManager"/>
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- other <bean/> definitions here -->
</beans>
Service声明注解:
@Transactional
public class DefaultFooService implements FooService {
Foo getFoo(String fooName);
Foo getFoo(String fooName, String barName);
void insertFoo(Foo foo);
void updateFoo(Foo foo);
}
注解驱动事务设置选项:
XML属性 | 注解属性 | 默认值 | 描述 |
transaction-manager | TransactionManagementConfigurer | transactionManager | 事务管理器使用的名字。 |
mode | mode | proxy | “proxy”:Spring AOP代理 “aspectj”:Spring的AspectJ织入 |
proxy-target-class | proxyTargetClass | false | 只用于代理模式。如果为true,使用基于类的代理,如果为false,使用基于标准JDK接口创建代理。 |
order | order | Ordered.LOWEST_PRECEDENCE | 定义事务通知顺序,没有定义,AOP系统自行决定。 |
属性 | 类型 | 默认值 | 描述 |
value | 字符串 | 指定事务管理器的可选限定符 | |
propagation | 枚举:Propagation | PROPAGATION_REQUIRED | 可选的事务传播设置 |
isolation | 枚举:Isolation | ISOLATION_DEFAULT | 可选的事务隔离级别 |
readOnly | boolean | 读/写 | 读/写 vs 只读 |
timeout | 秒 | -1 | 事务超时时间 |
rollbackFor | 任意派生自Throwable的Class对象数组 | 导致回滚的可选异常类数组 | |
rollbackForClassName | 任意派生自Throwable的类名数组 | 导致回滚的可选异常类名数组 | |
noRollbackFor | 任意派生自Throwable的Class对象数组 | 不导致回滚的可选异常类数组 | |
noRollbackForClassName | 任意派生自Throwable的类名数组 | 不导致回滚的可选异常类名数组 |
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>
自定义简称注解
@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 {
}
5.7 事务传播
PROPAGATION_REQUIRED当设置事务传播为PROPAGATION_REQUIRED时,为每个方法创建一个逻辑事务范围。每个逻辑事务范围能单独决定只回滚状态,外部事务范围逻辑独立于内部事务范围。当然,在标准的PROPAGATION_REQUIRED情况下,所有这些范围将映射相同的物理事务。因此,只回滚标记设置在内部事务范围不影响外部事务发生的实际提交。
然而,在这种情况下,内部事务范围设置为只回滚标记,外部事务没有决定回滚,因此回滚是意想不到的。相应的UnexpectedRollbackException抛出。
PROPAGATION_REQUIRES_NEW
对比PROPAGATION_REQUIRED,PROPAGATION_REQUIRES_NEW使用完全独立的事务。在这种情况下,底层物理事务是不同的,因此,能独自提交或回滚,外部事务的回滚状态不受内部事务的影响。
PROPAGATION_NESTED
使用一个能回滚到多个保存点的物理事务。这种回滚允许内部事务范围触发它的范围回滚,外部事务能够继续物理事务,尽管一些操作已经回滚。该设置通常映射到JDBC保存点,因此,只能用于JDBC资源事务。
6 编程式事务管理
Spring框架提供两种编程式事务管理:使用TransactionTemplate(推荐)
使用PlatformTransactionManager直接实现
6.1 使用TransactionTemplate
TransactionTemplate采用与其它Spring模板(例如,JdbcTemplate)相同的方式。public class SimpleService implements Service {
private final TransactionTemplate transactionTemplate;
public SimpleService(PlatformTransactionManager transactionManager) {
Assert.notNull(transactionManager, "The 'transactionManager' argument must not be null.");
this.transactionTemplate = new TransactionTemplate(transactionManager);
}
public Object someServiceMethod() {
return transactionTemplate.execute(new TransactionCallback() {
public Object doInTransaction(TransactionStatus status) {
updateOperation1();
return resultOfUpdateOperation2();
}
});
}
}
如果没有返回值,则使用TransactionCallbackWithoutResult类:
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(TransactionStatus status) {
updateOperation1();
updateOperation2();
}
});
在业务代码中回滚:
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(TransactionStatus status) {
try {
updateOperation1();
updateOperation2();
} catch (SomeBusinessExeption ex) {
[b]status.setRollbackOnly();[/b]
}
}
});
指定事务设置:
public class SimpleService implements Service {
private final TransactionTemplate transactionTemplate;
public SimpleService(PlatformTransactionManager transactionManager) {
Assert.notNull(transactionManager, "The 'transactionManager' argument must not be null.");
this.transactionTemplate = new TransactionTemplate(transactionManager);
this.transactionTemplate.setIsolationLevel(
TransactionDefinition.ISOLATION_READ_UNCOMMITTED);
this.transactionTemplate.setTimeout(30); // 30秒
}
}
使用Spring XML配置TransactionTemplate:
<bean id="sharedTransactionTemplate"
class="org.springframework.transaction.support.TransactionTemplate">
<property name="isolationLevelName" value="ISOLATION_READ_UNCOMMITTED"/>
<property name="timeout" value="30"/>
</bean>
6.2 使用PlatformTransactionManager
DefaultTransactionDefinition def = new DefaultTransactionDefinition();def.setName("SomeTxName");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = txManager.getTransaction(def);
try {
// 执行业务逻辑
} catch (MyException ex) {
txManager.rollback(status);
throw ex;
}
txManager.commit(status);
7 选择编程式还是声明式管理事务?
如果你只有少量事务操作选择编程式事务管理。8 事务绑定事件
从Spring 4.2开始,监听器事件可以绑定到事务阶段。@Componentpublic class MyComponent {
@TransactionalEventListener
public void handleOrderCreatedEvent(CreationEvent<Order> creationEvent) {
...
}
}
TransactionalEventListener注解暴露phase属性允许定制监听事务阶段。有效的阶段是:BEFORE_COMMIT、AFTER_COMMIT(默认)、AFTER_ROLLBACK和AFTER_COMPLETION。
相关文章推荐
- spring事务管理二:spring事务抽象
- spring事务管理二:spring事务抽象
- Spring备忘——事务管理(事务抽象)
- aop和spring事务处理
- Spring源码解析(一) Spring事务控制之Hibernate
- 今天用spring 事务出了一个很郁闷的问题
- Spring中的事务控制学习中
- 在spring中使用JDBC事务配置
- Spring整合hibernate:3、使用XML进行声明式的事务管理
- Spring中Hibernate的事务封装
- Spring事务配置的五种方式和spring里面事务的传播属性和事务隔离级别
- Spring 事务管理
- spring4+mybatis出现AOP方法结束后,事务不释放连接,导致连接数被用完的案例分析
- spring 中事务的PROPAGATION_REQUIRED,Readonly的解释
- AOP和Spring事务处理
- 【Spring】——事务实现过程及原理
- 详解基于spring多数据源动态调用及其事务处理
- Spring Cloud Commons 普通抽象
- spring 事务问题
- spring service事务传播