java ee传统事务有两种策略:
1.全局事务
JTA全局事务
Transaction ts = ctx.lookup(..);
//业务逻辑
tx.commit();
tx.rollback();
2.局部事务
2.1.JDBC局部事务
Connection conn = getConnection(..);
conn.setAutoCommit(false);
//业务逻辑
conn.commit();
conn.rollback();
2.2.Hibernate局部事务
Session s = getSession();
Transaction tx = s.beginTransaction();
//业务逻辑
tx.commit();
tx.rollback();
spring支持两种事务方式:编程式事务和声明式事务
spring事务策略是通过PlatformTransactionManager接口体现,源码如下:
public interface PlatfromTransactionManager{
//平台无关的获得事务方法
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
//平台无关的事务提交方法
void commit(TransactionStatus status) throws TransactionException;
//平台无关的事务回滚方法
void rollback(TransactionStatus status) throws TransactionException;
}
根据底层所使用的不同的持久化 API 或框架,PlatformTransactionManager 的主要实现类大致如下:
DataSourceTransactionManager:适用于使用JDBC和iBatis进行数据持久化操作的情况。
HibernateTransactionManager:适用于使用Hibernate进行数据持久化操作的情况。
JpaTransactionManager:适用于使用JPA进行数据持久化操作的情况。
另外还有JtaTransactionManager 、JdoTransactionManager、JmsTransactionManager等等。 如果我们使用JTA进行事务管理,我们可以通过 JNDI 和 Spring 的 JtaTransactionManager 来获取一个容器管理的 DataSource。JtaTransactionManager 不需要知道 DataSource 和其他特定的资源,因为它将使用容器提供的全局事务管理。而对于其他事务管理器,比如DataSourceTransactionManager,在定义时需要提供底层的数据源作为其属性,也就是
DataSource。与 HibernateTransactionManager 对应的是 SessionFactory,与 JpaTransactionManager 对应的是 EntityManagerFactory 等等
编程式事务管理:程序可以直接获取容器中的transactionManager Bean,该Bean总是PlatformTransactionManager的实例.
1.基于底层 API 的编程式事务管理的编程式事务管理的编程式事务管理的编程式事务管理 根据PlatformTransactionManager、TransactionDefinition 和 TransactionStatus 三个核心接口
2.基于 TransactionTemplate 的编程式事
清单6. 基于 TransactionTemplate 的事务管理示例代码 public class BankServiceImpl implements BankService { private BankDao bankDao; private TransactionTemplate transactionTemplate; ...... public boolean transfer(final Long fromId, final Long toId, final double amount) { return (Boolean) transactionTemplate.execute(new TransactionCallback(){ public Object doInTransaction(TransactionStatus status) { Object result; try { result = bankDao.transfer(fromId, toId, amount); } catch (Exception e) { status.setRollbackOnly(); result = false; System.out.println("Transfer Error!"); } return result; } }); } }
清单 7. 基于 TransactionTemplate 的事务管理示例配置文件 <bean id="bankService" class="footmark.spring.core.tx.programmatic.template.BankServiceImpl"> <property name="bankDao" ref="bankDao"/> <property name="transactionTemplate" ref="transactionTemplate"/> </bean>
声明式事务管理:无须在java程序中写任何事务操作代码,通过AOP方式织入增强处理.在目标方法执行之前,织入开始事务;在目标方法执行之后,织入结束事务,如果遇到异常,就回滚事务.
1.Spring 提供了 TransactionInterceptor类来实施声明式事务管理功能.
2.前面的声明式事务虽然好,但是却存在一个非常恼人的问题:配置文件太多。我们必须针对每一个目标对象配置一个 ProxyFactoryBean;另外,虽然可以通过父子Bean的方式来复用
TransactionInterceptor的配置,但是实际的复用几率也不高;这样,加上目标对象本身,每一个业务类可能需要对应三个 <bean/>配置,随着业务类的增多,配置文件将会变得越来越庞大,管理配置文件又成了问题。为了缓解这个问题,Spring为我们提供了
TransactionProxyFactoryBean,用于将TransactionInterceptor和 ProxyFactoryBean的配置合二为一
3.基于 <tx>命名空间的声明式事务管理
4.基于@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: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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!-- Proxool 数据源 --> <!-- <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName"> <value>org.logicalcobwebs.proxool.ProxoolDriver</value> </property> <property name="url"> <value>proxool.default</value> </property> </bean> --> <!-- C3P0 数据源 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${hibernate.connection.driver_class}"/> <property name="jdbcUrl" value="${hibernate.connection.url}"/> <property name="properties"> <props> <prop key="user">${hibernate.connection.username}</prop> <prop key="password">${hibernate.connection.password}</prop> </props> </property> </bean> <!-- SessionFactory --> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="packagesToScan" value="com.fox.addressbook.model"/> <!-- <property name="mappingResources"> <list> <value>XXX/XXX/XXX.hbm.xml</value> </list> </property> --> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">${hibernate.dialect}</prop> <prop key="hibernate.show_sql">${hibernate.show_sql}</prop> <prop key="hibernate.format_sql">${hibernate.format_sql}</prop> <prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop> <prop key="hibernate.cache.provider_class">${hibernate.cache.provider_class}</prop> <prop key="hibernate.cache.use_query_cache">${hibernate.cache.use_query_cache}</prop> <prop key="hibernate.cache.use_second_level_cache">${hibernate.cache.use_second_level_cache}</prop> </props> </property> </bean> <!-- HibernateTemplate --> <bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate"> <property name="sessionFactory" ref="sessionFactory"></property> </bean> <!-- 配置事务管理 --> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <!-- 配置注解实现管理事务(cglib:proxy-target-class="true") <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" /> --> <!-- enable the configuration of transactional behavior based on annotations <tx:annotation-driven transaction-manager="transactionManager"/>--> <!-- 指定使用cglib --> <!-- <aop:aspectj-autoproxy proxy-target-class="true" /> --> <!-- 配置事务的传播特性 --> <!--配置事务切面 ,用<aop:advice>启动自动事务管理 --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="add*" propagation="REQUIRED" /> <tx:method name="edit*" propagation="REQUIRED" /> <tx:method name="remove*" propagation="REQUIRED" /> <tx:method name="save*" propagation="REQUIRED" /> <tx:method name="update*" propagation="REQUIRED" /> <tx:method name="delete*" propagation="REQUIRED" /> <tx:method name="batchUpdate" propagation="REQUIRED" /> <tx:method name="defaultFolder" propagation="REQUIRED"/> <tx:method name="defaultFolder" propagation="REQUIRED" isolation="DEFAULT" no-rollback-for="" read-only="false" timeout="-1" rollback-for=""/> <tx:method name="*" read-only="true" /> </tx:attributes> </tx:advice> <!-- 配置事务的切入点 --> <!-- --> <aop:config> <aop:pointcut id="targetMethod" expression="execution(* com.fox.addressbook.dao.*.*(..))" /> <aop:advisor advice-ref="txAdvice" pointcut-ref="targetMethod" /> </aop:config> </beans>
指定事务属性的取值有较复杂的规则,这在 Spring中算得上是一件让人头疼的事。具体的书写规则如下:
传播行为 [,隔离级别] [,只读属性]
[,超时属性] [不影响提交的异常] [,导致回滚的异常]
传播行为是唯一必须设置的属性,其他都可以忽略,Spring为我们提供了合理的默认值。
传播行为的取值必须以“PROPAGATION_”开头,具体包括:PROPAGATION_MANDATORY、PROPAGATION_NESTED、PROPAGATION_NEVER、PROPAGATION_NOT_SUPPORTED、PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW、PROPAGATION_SUPPORTS,共七种取值。
隔离级别的取值必须以“ISOLATION_”开头,具体包括:ISOLATION_DEFAULT、ISOLATION_READ_COMMITTED、ISOLATION_READ_UNCOMMITTED、ISOLATION_REPEATABLE_READ、ISOLATION_SERIALIZABLE,共五种取值。
如果事务是只读的,那么我们可以指定只读属性,使用“readOnly”指定。否则我们不需要设置该属性。
超时属性的取值必须以“TIMEOUT_”开头,后面跟一个int类型的值,表示超时时间,单位是秒。