假设Service有两个A和B涉及到调用关系(ServiceA中方法内部调用ServiceB中的方法):
ServiceA { /** * 事务属性配置为 PROPAGATION_REQUIRED */ void methodA() { ServiceB.methodB(); } } ServiceB { /** * 事务属性配置为 PROPAGATION_REQUIRES_NEW */ void methodB() { } }
这种情况下, 因为 ServiceB#methodB 的事务属性为 PROPAGATION_REQUIRES_NEW, 所以两者不会发生任何关系, ServiceA#methodA 和 ServiceB#methodB 不会因为对方的执行情况而影响事务的结果, 因为它们根本就是两个事务, 在 ServiceB#methodB 执行时 ServiceA#methodA 的事务已经挂起了,所谓的挂起就是暂停methodA中事务的执行,等待ServiceB#methodB中事务执行完毕 再继续执行。不管ServiceB#methodB是执行成功提交还是失败回滚,都丝毫不影响ServiceA#methodA事务的执行和提交。不管ServiceA#methodA是成功执行还是失败回滚也不会影响 ServiceB#methodB的提交和执行(也就是说保证两个事务互不影响)
而PROPAGATION_NESTED嵌套事务如下所示:
ServiceA { /** * 事务属性配置为 PROPAGATION_REQUIRED */ void methodA() { ServiceB.methodB(); } } ServiceB { /** * 事务属性配置为 PROPAGATION_NESTED */ void methodB() { } }现在的情况就变得比较复杂了, ServiceB#methodB 的事务属性被配置为 PROPAGATION_NESTED, 此时两者之间又将如何协作呢? ServiceB#methodB 如果 rollback, 那么内部事务(即 ServiceB#methodB) 将回滚到它执行前的 SavePoint(注意, 嵌套事务中最核心的概念), 而外部事务(即 ServiceA#methodA) 可以有以下两种处理方式:
ServiceA { /** * 事务属性配置为 PROPAGATION_REQUIRED */ void methodA() { try { ServiceB.methodB(); } catch (SomeException) { // 执行其他业务, 如 ServiceC.methodC(); } } }这种方式也是嵌套事务最有价值的地方, 它起到了分支执行的效果, 如果 ServiceB.methodB 失败, 那么执行 ServiceC.methodC(), 而 ServiceB.methodB 已经回滚到它执行之前的 SavePoint, 所以不会产生脏数据(相当于此方法从未执行过), 这种特性可以用在某些特殊的业务中, 而 PROPAGATION_REQUIRED 和 PROPAGATION_REQUIRES_NEW 都没有办法做到这一点
2. 代码不做任何修改, 那么如果内部事务(即 ServiceB#methodB) rollback, 那么首先 ServiceB.methodB 回滚到它执行之前的 SavePoint(在任何情况下都会如此), 外部事务(即 ServiceA#methodA) 将根据具体的配置决定自己是 commit 还是 rollback (+MyCheckedException ).
嵌套事务与新事务的区别:
PROPAGATION_REQUIRES_NEW 启动一个新的, 不依赖于环境的 "内部" 事务. 这个事务将被完全 committed 或 rolled back 而不依赖于外部事务, 它拥有自己的隔离范围, 自己的锁, 等等. 当内部事务开始执行时, 外部事务将被挂起, 内务事务结束时, 外部事务将继续执行.
Spring的事务管理分为两类:
编程式事务管理:手动编写代码完成事务管理。在实际开发中很少使用,通过TransactionTemplate手动管理事务。
声明式事务管理:不需要手动编写代码,通过配置实现。开发中推荐使用(代码的侵入性最小)。Spring的声明式事务是通过AOP实现的。
事务操作环境的搭建
CREATE TABLE `account` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(20) NOT NULL, `money` double DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; INSERT INTO `account` VALUES ('1', 'aaa', '1000'); INSERT INTO `account` VALUES ('2', 'bbb', '1000'); INSERT INTO `account` VALUES ('3', 'ccc', '1000');
创建一个WEB项目
导入相应的jar包
开发Spring必须的jar包
日志相关的jar
数据库相关的jar
测试包
添加配置文件applicationContextX.xml、jdbc.properties、log4j.properties
jdbc.properties
jdbc.driver = com.mysql.jdbc.Driver jdbc.url = jdbc:mysql:///spring3_day03 jdbc.user = root jdbc.password =rootlog4j.properties
### direct log messages to stdout ### log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.Target=System.err log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n ### direct messages to file mylog.log ### log4j.appender.file=org.apache.log4j.FileAppender log4j.appender.file.File=c\:mylog.log log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n ### set log levels - for more verbose logging change 'info' to 'debug' ### log4j.rootLogger=info, stdout创建类
AccountService、AccountDao以及实现类
在Spring中进行注册
<!-- 业务层类 --> <bean id="accountService" class="spring3.transaction.demo.AccountServiceImpl"> <!-- 在业务层注入Dao --> <property name="accountDao" ref="accountDao"/> </bean> <!-- 持久层类 --> <bean id="accountDao" class="spring3.transaction.demo1X.AccountDaoImpl"> <!-- 注入连接池的对象,通过连接池对象创建模板. --> <property name="dataSource" ref="dataSource"/> </bean>
<!-- 配置事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 需要注入连接池,通过连接池获得连接 --> <property name="dataSource" ref="dataSource"/> </bean>第二步: 注册事务模板类(在事务模板类中还可以注入事务的隔离级别以及事务的传播行为等信息)
<!-- 事务管理的模板 --> <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"> <property name="transactionManager" ref="transactionManager"/> </bean>第三步: 在业务层注入模板类(由模板类管理事务)
<!-- 业务层类 --> <bean id="accountService" class="spring3.transaction.demo1.AccountServiceImpl"> <!-- 在业务层注入Dao --> <property name="accountDao" ref="accountDao"/> <!-- 在业务层注入事务的管理模板 --> <property name="transactionTemplate" ref="transactionTemplate"/> </bean>第四步: 在业务层代码上使用模板
public void transfer(final String from, final String to, final Double money) { transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus status) { accountDao.out(from, money); int d = 1 / 0; accountDao.in(to, money); } }); }手动编码方式的缺点: 代码量增加,代码有侵入性。
package spring3.transaction.demo1; public interface AccountDao { /** * 转出的方法 * @param from :转出的人 * @param money :要转账的金额 */ public void out(String from, Double money); /** * 转入的方法 * @param to :转入的人 * @param money :要转账的金额 */ public void in(String to, Double money); }AccountDaoImpl
package spring3.transaction.demo1; import java.util.jar.Attributes.Name; import org.springframework.jdbc.core.support.JdbcDaoSupport; public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao { /** * 转出的方法 * @param from :转出的人 * @param money :要转账的金额 */ public void out(String from, Double money){ String sql = "update account set money = money - ? where name = ?"; this.getJdbcTemplate().update(sql, money,from); } /** * 转入的方法 * @param to :转入的人 * @param money :要转账的金额 */ public void in(String to, Double money){ String sql = "update account set money = money + ? where name = ?"; this.getJdbcTemplate().update(sql, money,to); } }AccountService
package spring3.transaction.demo1; public interface AccountService { /** * 转账的方法 * @param from:从哪转出 * @param to:转入给谁 * @param money:转账的金额 */ public void transfer(String from, String to, Double money); }AccountServiceImpl
package spring3.transaction.demo1; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; public class AccountServiceImpl implements AccountService { private AccountDao accountDao; private TransactionTemplate transactionTemplate; public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } public void setTransactionTemplate(TransactionTemplate transactionTemplate) { this.transactionTemplate = transactionTemplate; } /** * 转账的方法 * * @param from:从哪转出 * @param to:转入给谁 * @param money:转账的金额 */ public void transfer(final String from, final String to, final Double money) { transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus arg0) { accountDao.out(from, money); int d=1/0; accountDao.in(to, money); } }); } }在src下新建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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 引入外部的属性文件 --> <context:property-placeholder location="classpath:jdbc.properties" /> <!-- 配置c3p0连接池 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driver}" /> <property name="jdbcUrl" value="${jdbc.url}" /> <property name="user" value="${jdbc.user}" /> <property name="password" value="${jdbc.password}" /> </bean> <!-- 业务层类 --> <bean id="accountService" class="spring3.transaction.demo1.AccountServiceImpl"> <!-- 在业务层注入Dao --> <property name="accountDao" ref="accountDao" /> <!-- 在业务层注入事务的管理模板 --> <property name="transactionTemplate" ref="transactionTemplate" /> </bean> <!-- 持久层类 --> <!-- 注入DataSource数据源 JdbcDaoSuppor会自动检测JdbcTemplate是否设置,如果没有则自动设置 --> <bean id="accountDao" class="spring3.transaction.demo1.AccountDaoImpl"> <!-- 注入连接池对象,通过连接池对象创建JDBC模板 --> <property name="dataSource" ref="dataSource" /> </bean> <!-- 事务管理模板 --> <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"> <!-- 注入事务管理器 必须注入的属性: 事务管理器--> <property name="transactionManager" ref="transactionManager" /> <property name="isolationLevelName" value="ISOLATION_DEFAULT" /> <property name="propagationBehaviorName" value="PROPAGATION_REQUIRED"/> </bean> <!-- 配置事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 需要注入连接池,通过连接池获取连接 --> <property name="dataSource" ref="dataSource" /> </bean> </beans>测试代码如下 SpringTest1
package spring3.transaction.demo1; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class SpringTest1 { @Autowired @Qualifier("accountService") private AccountService accountService; @Test public void demo1() { // 完成转账操作 accountService.transfer("aaa", "bbb", 100d); } }发现往Service中注入事务模板后 执行时 代码回滚 查询表如下:
public abstract class JdbcDaoSupport extends DaoSupport { private JdbcTemplate jdbcTemplate; /** * Set the JDBC DataSource to be used by this DAO. */ public final void setDataSource(DataSource dataSource) { if (this.jdbcTemplate == null || dataSource != this.jdbcTemplate.getDataSource()) { this.jdbcTemplate = createJdbcTemplate(dataSource); initTemplateConfig(); } } ...... }可以发现向继承自JdbcDaoSupport类的Dao注入DataSource时 会判断当前Dao的JdbcTemplate模板是否存在,如果不存在就会自动创建模板并完成初始化操作。
<!-- 事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 注入连接池 --> <property name="dataSource" ref="dataSource"/> </bean>第二步: 创建业务层代理对象
<!-- 配置生成代理对象 --> <bean id="accountServiceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <!-- 目标对象 --> <property name="target" ref="accountService"/> <!-- 注入事务管理器 --> <property name="transactionManager" ref="transactionManager"/> <!-- 事务的属性设置 --> <property name="transactionAttributes"> <props> <prop key="transfer">PROPAGATION_REQUIRED</prop> </props> </property> </bean>第三步: 编写测试类
@Autowired @Qualifier("accountServiceProxy") private AccountService accountService;prop格式: PROPAGATION,ISOLATION,readOnly,-Exception,+Exception
package spring3.transaction.demo2; public interface AccountDao { /** * 转出的方法 * @param from :转出的人 * @param money :要转账的金额 */ public void out(String from, Double money); /** * 转入的方法 * @param to :转入的人 * @param money :要转账的金额 */ public void in(String to, Double money); }AccountDaoImpl
package spring3.transaction.demo2; import org.springframework.jdbc.core.support.JdbcDaoSupport; public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao { /** * 转出的方法 * @param from :转出的人 * @param money :要转账的金额 */ public void out(String from, Double money){ String sql = "update account set money = money - ? where name = ?"; this.getJdbcTemplate().update(sql, money,from); } /** * 转入的方法 * @param to :转入的人 * @param money :要转账的金额 */ public void in(String to, Double money){ String sql = "update account set money = money + ? where name = ?"; this.getJdbcTemplate().update(sql, money,to); } }AccountService
package spring3.transaction.demo2; public interface AccountService { /** * 转账的方法 * @param from:从哪转出 * @param to:转入给谁 * @param money:转账的金额 */ public void transfer(String from, String to, Double money); }AccountServiceImpl
package spring3.transaction.demo2; public class AccountServiceImpl implements AccountService { private AccountDao accountDao; public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } /** * 转账的方法 * * @param from :从哪转出 * @param to :转入的人 * @param money:转账金额 */ public void transfer(String from, String to, Double money) { accountDao.out(from, money); int d=1/0; accountDao.in(to, money); } }在src下新建applicationContext2.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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 引入外部的属性文件 --> <context:property-placeholder location="classpath:jdbc.properties" /> <!-- 配置c3p0连接池 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driver}" /> <property name="jdbcUrl" value="${jdbc.url}" /> <property name="user" value="${jdbc.user}" /> <property name="password" value="${jdbc.password}" /> </bean> <!-- 业务层类 --> <bean id="accountService" class="spring3.transaction.demo2.AccountServiceImpl"> <!-- 在业务层注入Dao --> <property name="accountDao" ref="accountDao" /> </bean> <!-- 持久层类 --> <bean id="accountDao" class="spring3.transaction.demo2.AccountDaoImpl"> <!-- 注入连接池对象,通过连接池对象创建模板 --> <property name="dataSource" ref="dataSource" /> </bean> <!-- 事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 注入连接池 --> <property name="dataSource" ref="dataSource" /> </bean> <!-- 配置生成代理对象 --> <bean id="accountServiceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <!-- 目标对象 --> <property name="target" ref="accountService" /> <!-- 注入事务管理器对象 --> <property name="transactionManager" ref="transactionManager" /> <!-- 事务的属性设置 --> <property name="transactionAttributes"> <props> <!-- * 表示对目标对象的中的所有方法进行增强 --> <prop key="transfer">PROPAGATION_REQUIRED,ISOLATION_DEFAULT,+java.lang.ArithmeticException</prop> </props> </property> </bean> </beans>新建测试类SpringTest2
package spring3.transaction.demo2; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext2.xml") public class SpringTest2 { @Autowired @Qualifier("accountServiceProxy") private AccountService accountService; @Test public void demo1() { accountService.transfer("aaa", "bbb", 100d); } }由于配置了一个除法异常提交事务的属性 所以当执行到异常时就会提交事务
<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">第三步: 注册事务管理器
<!-- 事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean>第四步:定义增强(事务管理)
<!-- 定义一个增强 --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <!-- 增强(事务)的属性的配置 --> <tx:attributes> <!-- isolation:DEFAULT :事务的隔离级别. propagation :事务的传播行为. read-only :false.不是只读 timeout :-1 no-rollback-for :发生哪些异常不回滚 rollback-for :发生哪些异常回滚事务 --> <tx:method name="transfer"/> </tx:attributes> </tx:advice>第五步: 定义AOP的配置(切点和通知的组合)
<!-- aop配置定义切面和切点的信息 --> <aop:config> <!-- 定义切点:哪些类的哪些方法应用增强 --> <aop:pointcut expression="execution(* spring3.transaction.demo3.AccountService+.*(..))" id="mypointcut"/> <!-- 定义切面: --> <aop:advisor advice-ref="txAdvice" pointcut-ref="mypointcut"/> </aop:config>第六步: 编写测试类
package spring3.transaction.demo3; public interface AccountDao { /** * 转出的方法 * @param from :转出的人 * @param money :要转账的金额 */ public void out(String from, Double money); /** * 转入的方法 * @param to :转入的人 * @param money :要转账的金额 */ public void in(String to, Double money); }
package spring3.transaction.demo3; import org.springframework.jdbc.core.support.JdbcDaoSupport; public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao { /** * 转出的方法 * @param from :转出的人 * @param money :要转账的金额 */ public void out(String from, Double money){ String sql = "update account set money = money - ? where name = ?"; this.getJdbcTemplate().update(sql, money,from); } /** * 转入的方法 * @param to :转入的人 * @param money :要转账的金额 */ public void in(String to, Double money){ String sql = "update account set money = money + ? where name = ?"; this.getJdbcTemplate().update(sql, money,to); } }
package spring3.transaction.demo3; public interface AccountService { /** * 转账的方法 * @param from:从哪转出 * @param to:转入给谁 * @param money:转账的金额 */ public void transfer(String from, String to, Double money); }AccountServiceImpl
package spring3.transaction.demo3; public class AccountServiceImpl implements AccountService { private AccountDao accountDao; public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } /** * 转账的方法 * * @param from * :从哪转出 * @param to * :转入的人 * @param money:转账金额 */ public void transfer(String from, String to, Double money) { accountDao.out(from, money); // int d=1/0; accountDao.in(to, money); } }在src下新建配置文件applicationContext3.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" 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"> <!-- 引入外部属性文件 --> <context:property-placeholder location="classpath:jdbc.properties" /> <!-- 配置c3p0连接池 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driver}" /> <property name="jdbcUrl" value="${jdbc.url}" /> <property name="user" value="${jdbc.user}" /> <property name="password" value="${jdbc.password}" /> </bean> <!-- 业务层类 --> <bean id="accountService" class="spring3.transaction.demo3.AccountServiceImpl"> <!-- 在业务层注入Dao --> <property name="accountDao" ref="accountDao" /> </bean> <!-- 持久层类 --> <bean id="accountDao" class="spring3.transaction.demo3.AccountDaoImpl"> <!-- 注入连接池对象,通过连接池对象创建模板 --> <property name="dataSource" ref="dataSource" /> </bean> <!-- 事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <!-- 定义一个增强 --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <!-- 增强事务的属性配置 --> <!-- isolation:DEFAULT :事务的隔离级别. propagation :事务的传播行为. read-only :false.不是只读 timeout :-1 no-rollback-for :发生哪些异常不回滚 rollback-for :发生哪些异常回滚事务 --> <tx:attributes> <tx:method name="transfer" isolation="DEFAULT" propagation="REQUIRED" read-only="false" /> </tx:attributes> </tx:advice> <!-- aop配置定义切面和切点信息 --> <aop:config> <!-- 定义切点: 哪些类的哪些方法应用增强 --> <aop:pointcut expression="execution(* spring3.transaction.demo3.AccountService+.*(..))" id="myPointcut" /> <!-- 定义切面 --> <aop:advisor advice-ref="txAdvice" pointcut-ref="myPointcut" /> </aop:config> </beans>新建测试类SpringTest3
package spring3.transaction.demo3; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; /** * 声明式事务的使用:基于切面的 * @author liuxun * */ import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext3.xml") public class SpringTest3 { @Autowired @Qualifier("accountService") AccountService accountService; @Test public void demo1() { accountService.transfer("aaa", "bbb", 100d); } }打开AccountServiceImpl中被屏蔽的异常 测试如下
<!-- 事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean>第二步: 注解事务
<!-- 开启注解的事务管理 --> <tx:annotation-driven transaction-manager="transactionManager"/>第三步: 在Service上使用注解@Transactional
package spring3.transaction.demo4; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED, readOnly = false) public class AccountServiceImpl implements AccountService { private AccountDao accountDao; public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } /** * 转账的方法 * * @param from * :从哪转出 * @param to * :转入的人 * @param money:转账金额 */ public void transfer(String from, String to, Double money) { accountDao.out(from, money); // int d=1/0; accountDao.in(to, money); } }在src下新建applicationContext4.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" 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"> <!-- 引入外部属性文件 --> <context:property-placeholder location="classpath:jdbc.properties" /> <!-- 配置c3p0连接池 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driver}" /> <property name="jdbcUrl" value="${jdbc.url}" /> <property name="user" value="${jdbc.user}" /> <property name="password" value="${jdbc.password}" /> </bean> <!-- 业务层类 --> <bean id="accountService" class="spring3.transaction.demo4.AccountServiceImpl"> <!-- 在业务层注入Dao --> <property name="accountDao" ref="accountDao" /> </bean> <!-- 持久层类 --> <bean id="accountDao" class="spring3.transaction.demo4.AccountDaoImpl"> <!-- 注入连接池对象,通过连接池对象创建模板 --> <property name="dataSource" ref="dataSource" /> </bean> <!-- 事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <!-- 开启注解的事务管理 --> <!-- 若不配置 "transaction-manager"属性,默认查找名称为transactionManager的事务管理器Bean --> <tx:annotation-driven transaction-manager="transactionManager" /> </beans>新建SpringTest4测试类
package spring3.transaction.demo4; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; /** * 声明式事务的使用:基于切面的 * @author liuxun * */ import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext4.xml") public class SpringTest4 { @Autowired @Qualifier("accountService") AccountService accountService; @Test public void demo1() { accountService.transfer("aaa", "bbb", 100d); } }运行