一、前言
Spring对于事务有下面几种实现方式:
1. 编程式事务
2. 声明式事务
2.1 基于TransactionProxyFactoryBean生成对象的代理类,从而实现事务操作的增强操作
2.2 基于AspectJ的XML配置方式
2.3 基于注解方式
以转账功能来搭建项目环境
二、准备
* Oracle数据库表
create table account (accountNO VARCHAR(32) PRIMARY KEY NOT NULL, money Number(5,2) default 0.00);
插入相关数据:
insert into account(accountNO, money) values('zhangsan', 100.00);
insert into account(accountNO, money) values('lisi', 100.00);
insert into account(accountNO, money) values('wangwu', 100.00);
* 准备项目jar包环境
Oracle数据库驱动包:
ojdbc6.jar
Spring基本包及相应依赖包:
spring-beans-4.0.4.RELEASE.jar
spring-context-4.0.4.RELEASE.jar
spring-expression-4.0.4.RELEASE.jar
commons-logging-1.1.3.jar
spring-core-4.0.4.RELEASE.jar
Spring AOP功能依赖包:
spring-aop-4.0.4.RELEASE.jar -- Spring aop功能包
aopalliance-1.0.jar -- aop联盟包(aop标准包)
Spring AspectJ支持包:
spring-aspects-4.0.4.RELEASE.jar -- Spring 针对AspectJ的支持扩展包
aspectjweaver-1.6.11.jar -- AspectJ实现包
Spring jdbc包:
spring-jdbc-4.0.4.RELEASE.jar --支持jdbc
Spring事务支持包:
spring-tx-4.0.4.RELEASE.jar -- Spring事务功能包
Junit测试包:
spring-test-4.0.4.RELEASE.jar -- Spring测试相关包
junit-4.11.jar -- Junit4包
数据库连接实现包:
commons-dbcp2-2.0.jar -- apache连接池包
commons-pool2-2.2.jar -- dbcp2依赖包
* 编写转账业务层接口:AccountService
public interface AccountService { /** * 账户转账操作 * @param out 转出账户 * @param in 转入账户 * @param money 转账金额 */ void transfer(String out, String in, double money); }* 编写转账DAO层接口: AccountDao
public interface AccountDao { /** * 账户转入 * @param in 转入账户 * @param money 转入金额 */ void inMoney(String in, double money); /** * 账户转出 * @param out 转出账户 * @param money 转出金额 */ void outMoney(String out, double money); }
* 配置datasources.properties数据库文件
oracle.driverClassName=oracle.jdbc.driver.OracleDriver oracle.userName=heng oracle.password=ping1126 oracle.url=jdbc:oracle:thin:@//127.0.0.1:1521/orcl
* 配置applicationContext.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:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd "> <context:property-placeholder location="classpath:datasources.properties" /> <!-- 数据库连接 --> <bean id="datasource" class="org.apache.commons.dbcp2.BasicDataSource"> <property name="driverClassName" value="${oracle.driverClassName}" /> <property name="username" value="${oracle.userName}" /> <property name="password" value="${oracle.password}" /> <property name="url" value="${oracle.url}" /> </bean> <bean id="accountDao" class="codes.ebo.study.spring.transaction.demo1.AccountDaoImpl"> <property name="dataSource" ref="datasource" /> </bean> <bean id="accountService" class="codes.ebo.study.spring.transaction.demo1.AccountServiceImpl"> <property name="accountDao" ref="accountDao" /> </bean> </beans>其中涉及到业务层接口和DAO接口的实现类:
业务层接口实现类: AccountServiceImpl
public class AccountServiceImpl implements AccountService { private AccountDao accountDao; public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } @Override public void transfer(String out, String in, double money) { accountDao.outMoney(out, money); accountDao.inMoney(in, money); } }DAO层接口实现类: AccountDaoImpl
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao { /** * 账户转入 * * @param in 转入账户 * @param money 转入金额 */ @Override public void inMoney(String in, double money) { this.getJdbcTemplate().update("update account set money = money + ? where accountNO = ?", new Object[] { money, in }, new int[] {Types.DOUBLE, Types.VARCHAR}); } /** * 账户转出 * * @param out 转出账户 * @param money 转出金额 */ @Override public void outMoney(String out, double money) { this.getJdbcTemplate().update("update account set money = money - ? where accountNO = ?", new Object[] { money, out }, new int[] {Types.DOUBLE, Types.VARCHAR}); } }
三、 Spring事务管理实现
3.1. 编程式事务实现
Spring不直接管理事务,它提供很多可供选择的事务管理器,将事务管理的责任委托给由JTA或相应的持久性机制所提供的某个特定平台事务实现。针对mybatis和JDBC,我们可以使用org.springframework.jdbc.datasource.DataSourceTransactionManager进行管理。
在applicationContext.xml的accountDao的bean定义之上加上transactionManager配置, 修改后改成applicationContext_demo1.xml:
<!-- Spring事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="datasource" /> </bean>
另外, Spring为了方便,使用org.springframework.transaction.support.TransactionTemplate来对事务相关代码进行优化,所以加上TransactionTemplate模板对象配置并加入到业务层实现类AccountServiceImpl上去:
<!-- 编程式事务模板 --> <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"> <property name="transactionManager" ref="transactionManager" /> </bean>
<bean id="userService" class="codes.ebo.study.spring.transaction.demo1.AccountServiceImpl"> <property name="accountDao" ref="accountDao" /> <property name="transactionTemplate" ref="transactionTemplate" /> </bean>
业务层实现类AccountServiceImpl代码修改如下:
public class AccountServiceImpl implements AccountService { private AccountDao accountDao; private TransactionTemplate transactionTemplate; public void setTransactionTemplate(TransactionTemplate transactionTemplate) { this.transactionTemplate = transactionTemplate; } public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } @Override public void transfer(String out, String in, double money) { transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus status) { accountDao.outMoney(out, money); accountDao.inMoney(in, money); } }); } }编写相关测试类:
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = {"classpath:applicationContext_demo1.xml"}) public class AccountTest { @Resource(name="accountService") private AccountService accountService; @Test public void testTransfer() { accountService.transfer("zhangsan", "lisi", 20d); } }
3.2. 声明式事务实现
a. 基于TransactionProxyFactoryBean生成对象的代理类
在准备环境上的applicationContext.xml配置上Spring事务管理器和TransactionProxyFactoryBean, 修改后改成applicationContext_demo2.xml
<!-- Spring事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="datasource" /> </bean> <bean id="accountDao" class="codes.ebo.study.spring.transaction.demo2.AccountDaoImpl"> <property name="dataSource" ref="datasource" /> </bean> <bean id="accountService" class="codes.ebo.study.spring.transaction.demo2.AccountServiceImpl"> <property name="accountDao" ref="accountDao" /> </bean> <bean id="accountServiceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="target" ref="accountService" /> <property name="proxyInterfaces" value="codes.ebo.study.spring.transaction.demo2.AccountService" /> <property name="transactionManager" ref="transactionManager" /> <property name="transactionAttributes"> <props> <prop key="transfer">PROPAGATION_REQUIRED</prop> </props> </property> </bean>相关测试类:
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = {"classpath:applicationContext_demo2.xml"}) public class AccountTest { @Resource(name="accountServiceProxy") private AccountService accountService; @Test public void testTransfer() { accountService.transfer("zhangsan", "lisi", 20d); } }
b. 基于AspectJ的XML配置方式
在准备环境上的applicationContext.xml配置上事务管理器、事务增强和切面信息, 修改后改成applicationContext_demo3.xml:
<!-- Spring事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="datasource" /> </bean> <!-- 事务的通知:(事务增强) --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="transfer" propagation="REQUIRED" /> </tx:attributes> </tx:advice> <!-- 配置切面 --> <aop:config> <aop:pointcut id="txPointcut" expression="execution(* codes.ebo.study.spring.transaction.demo3.AccountServiceImpl.*(..))" /> <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut" /> </aop:config>
c. 基于注解(Transactional)方式
在准备环境上的applicationContext.xml配置上事务管理器、相关事务注解配置,, 修改后改成applicationContext_demo4.xml
<!-- Spring事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="datasource" /> </bean> <!-- 开启Spring事务注解功能 --> <tx:annotation-driven transaction-manager="transactionManager" />
同时在业务层实现类AccountServiceImpl类层面或者方法层面上加上Transactional注解信息
public class AccountServiceImpl implements AccountService { private AccountDao accountDao; public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } @Override @Transactional(propagation= Propagation.REQUIRED) public void transfer(String out, String in, double money) { accountDao.outMoney(out, money); accountDao.inMoney(in, money); } }
四、总结
1. 2.2 和 2.3 这两种方式在项目中比较常用, 编程式和通过TransactionProxyFactoryBean这两种方式的使用场景比较少
2. 针对事务的属性
a: readOnly: 表示以读方式进行事务操作,当设置为true时但操作又涉及到对表数据进行更新(包含新增和删除),方法调用就会出现异常
b: rollbackFor或者rollback-for, 其值为异常集合,表示执行事务方法的时候出现集合中的异常时,会进行整个事务回滚
c: noRollbackFor或者no-rollback-for,其值为异常集合,表示执行事务方法的时候出现集合中的异常时,不会进行整个事务回滚,可能会造成数据不一致后果
d: timeout事务执行事件,默认为-1,表示无时间限制,配置一定数值单位为秒,超时后事务回滚