事务:
- 原子性: 事务不可分割。
- 一致性: 事务执行的前后,数据完整性保持一致。
- 隔离性: 一个事务执行的时候,不应该受到其他事务的干扰。
- 持久性: 一旦结束,数据就永久的保存到了数据库。
- 脏读: 一个事务读到了另一个事务未提交的数据。
- 不可重复读: 一个事务读到了另一个事务已经提交的数据(update) 导致一个事务中多次查询结果不一致。
- 虚读: 一个事务读到了另一个事务已经提交的数据(insert) 到最后一个事务多次查询结果不一致。
- 未提交读(Read uncommitted): 以上情况都有可能发生。
- 已提交读(Read committed): 避免脏读,但是不可重复读、虚读有可能发生。
- 可重复读(Read Repeatable): 避免脏读,不可重复读,但是虚读有可能发生。
- 串行的(Serizable): 避免以上所有情况。
Spring提供的事务管理的API
- PlatformTransactionManager 事务管理器
- TransactionDefinition 事务定义信息(隔离、传播‘超时、只读)
- TransactionStatus 事务具体运行状态
- commit(TransactionStatus status)
- getTransaction(TransactionDefinition definition)
- rollback(TransactionStatus status)
- ISOLATION_XXX: 事务隔离级别。
- PROPAGATION_XXX: 事务的传播行为(不是JDBC中特有的,为了解决实际开发的问题)。
- 是否有保存点
- 是否是一个新的事务
- 事务是否已经提交
PlatformTransactionManager
TransactionDefinition(定义事务的隔离级别和传播行为)
事务的隔离级别: 分为4种
事务的传播行为(七种)
假设Service有两个A和B涉及到调用关系(ServiceA中方法内部调用ServiceB中的方法):
- PROPAGATION_REQUIRED:支持当前的事务,如果不存在就创建一个。如果A有事务,B使用A的事务,如果A没有事务,B就开启一个新的事务(保证A和B在同一个事务中)。
- PROPAGATION_SUPPORTS:支持当前事务,如果不存在就不使用事务。A、B:如果A有事务,B使用A的事务,如果A没有事务,B就不使用事务。
- PROPAGATION_MANDATORY:支持当前的事务,如果不存在就会抛出异常。A、B: 如果A有事务,B使用A的事务,如果A没有事务则抛出异常。
- PROPAGATION_REQUIRES_NEW:如果有事务存在,则挂起当前事务,创建一个新的事务。A、B: 如果A有事务,B将A的事务挂起,重新创建一个新的事务(保证A和B不在同一个事务中)。
- PROPAGATION_NOT_SUPPORTED:以非事务的方式运行,如果有事务存在,挂起当前的事务。A、B:非事务的方式运行,A有事务就会挂起当前的事务。
- PROPAGATION_NEVER:以非事务的方式运行,如果有事务存在则抛出异常。
- PROPAGATION_NESTED:如果当前事务存在,则嵌套事务执行。它基于SavePoint(保存点)技术。A、B: A有事务,A执行事务之后的内容保存到SavePoint,若B事务有异常,由自己设置事务提交还是回滚。
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) 可以有以下两种处理方式:1. 改写 ServiceA 如下
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 而不依赖于外部事务, 它拥有自己的隔离范围, 自己的锁, 等等. 当内部事务开始执行时, 外部事务将被挂起, 内务事务结束时, 外部事务将继续执行.
另一方面, PROPAGATION_NESTED 开始一个 "嵌套的" 事务, 它是已经存在事务的一个真正的子事务. 嵌套事务开始执行时, 它将取得一个 savepoint. 如果这个嵌套事务失败, 我们将回滚到此 savepoint. 嵌套事务是外部事务的一部分, 只有外部事务结束后它才会被提交.
由此可见, PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED 的最大区别在于, PROPAGATION_REQUIRES_NEW 完全是一个新的事务, 而 PROPAGATION_NESTED 则是外部事务的子事务, 如果外部事务 commit, 嵌套事务也会被 commit, 这个规则同样适用于 roll back.
Spring中的事务管理方式:
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包
- spring-beans-3.2.0.RELEASE.jar
- spring-context-3.2.0.RELEASE.jar
- spring-core-3.2.0.RELEASE.jar
- spring-expression-3.2.0.RELEASE.jar
日志相关的jar
- com.springsource.org.apache.commons.logging-1.1.1.jar
- com.springsource.org.apache.log4j-1.2.15.jar
数据库相关的jar
- spring-jdbc-3.2.0.RELEASE.jar (支持JDBC的包)
- spring-tx-3.2.0.RELEASE.jar (涉及事务的包)
- com.springsource.com.mchange.v2.c3p0-0.9.1.2.jar (c3p0连接池的包)
- mysql-connector-java-5.0.4-bin.jar (数据库驱动)
测试包
- spring-test-3.2.0.RELEASE.jar (spring整合Junit4的测试包)
添加配置文件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 =root
log4j.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中注入事务模板后 执行时 代码回滚 查询表如下:
屏蔽掉service实现类中的异常代码(int d=1/0;)后运行
成功转账 如果不屏蔽异常 且不使用事务模板 则异常上面的代码能执行 出现了异常就会抛出 无法执行下面的转入代码,就会造成一方的钱被扣掉了,另一方还没收到钱。
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);
}
}
由于配置了一个除法异常提交事务的属性 所以当执行到异常时就会提交事务
方式三:声明式事务管理(自动代理:基于切面Aspect********)
开发步骤如下:新建package demo3<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);
}
}
运行