目录
[1. 全配置文件](#1. 全配置文件)
[1.1 导入pom.xml的依赖](#1.1 导入pom.xml的依赖)
[1.2 编写applicationContext.xml](#1.2 编写applicationContext.xml)
[1.3 编写Account实体类](#1.3 编写Account实体类)
[1.4 编写AccountDao接口](#1.4 编写AccountDao接口)
[1.5 编写AccountDaoImpl实现类](#1.5 编写AccountDaoImpl实现类)
[1.6 编写AccountService接口](#1.6 编写AccountService接口)
[1.7 编写AccountServiceImpl实现类](#1.7 编写AccountServiceImpl实现类)
[1.8 编写TransactionTest测试类](#1.8 编写TransactionTest测试类)
[2. 半注解的方式](#2. 半注解的方式)
[2.1 编写xml配置文件](#2.1 编写xml配置文件)
[2.2 Account实体类](#2.2 Account实体类)
[2.3 AccountDao](#2.3 AccountDao)
[2.4 AccountDaoImpl](#2.4 AccountDaoImpl)
[2.5 AccountService](#2.5 AccountService)
[2.6 AccountServiceImpl](#2.6 AccountServiceImpl)
[2.7 Test类](#2.7 Test类)
[3. 全注解的模式](#3. 全注解的模式)
[3.1 编写配置类SpringConfig](#3.1 编写配置类SpringConfig)
[3.2 AccountDao](#3.2 AccountDao)
[3.3 AccountDaoImpl](#3.3 AccountDaoImpl)
[3.4 AccountService](#3.4 AccountService)
[3.5 AccountServiceImpl](#3.5 AccountServiceImpl)
[3.6 Test测试类](#3.6 Test测试类)
[1. 核心概念](#1. 核心概念)
[2. 七种传播行为](#2. 七种传播行为)
[2.1 REQUIRED(默认)](#2.1 REQUIRED(默认))
[2.1.1 特点](#2.1.1 特点)
[2.2 REQUIRES_NEW](#2.2 REQUIRES_NEW)
[2.2.1 特点](#2.2.1 特点)
[2.2.2 场景](#2.2.2 场景)
[2.3 SUPPORTS](#2.3 SUPPORTS)
[2.3.1 特点](#2.3.1 特点)
[2.3.2 场景](#2.3.2 场景)
[2.4 MANDATORY](#2.4 MANDATORY)
[2.4.1 特点](#2.4.1 特点)
[2.4.2 场景](#2.4.2 场景)
[2.5 NOT_SUPPORTED](#2.5 NOT_SUPPORTED)
[2.5.1 特点](#2.5.1 特点)
[2.5.2 场景](#2.5.2 场景)
[2.6 NEVER](#2.6 NEVER)
[2.6.1 特点](#2.6.1 特点)
[2.6.2 场景](#2.6.2 场景)
[2.7 NESTED(嵌套事务)](#2.7 NESTED(嵌套事务))
[2.7.1 特点](#2.7.1 特点)
[2.7.2 注意](#2.7.2 注意)
[3. 七种事务传播行为总结](#3. 七种事务传播行为总结)
一、Spring框架的事务管理
相比于自己写事务,Spring框架来管理事务更方便,它也有三种方式
全配置文件
半注解
全注解
1. 全配置文件
1.1 导入pom.xml的依赖
XML
<dependencies>
<!-- Spring 核心 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!-- Spring JDBC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!-- Spring 事务 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!-- AOP 相关 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
<!-- MySQL 驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
<!-- Druid 连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<!-- 测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
<scope>test</scope>
</dependency>
<!-- 日志 -->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
</dependencies>
1.2 编写applicationContext.xml
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: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/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">
<!--纯xml配置-->
<!-- 1. 数据源 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///spring_db"/>
<property name="username" value="root"/>
<property name="password" value="2020"/>
</bean>
<!-- 2. 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 3. 事务通知(Spring 提供) -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="pay" propagation="REQUIRED"/>
<tx:method name="find*" read-only="true"/>
</tx:attributes>
</tx:advice>
<!-- 4. AOP 配置:把事务织入 -->
<aop:config>
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.qcby.service.AccountServiceImpl.*(..))"/>
</aop:config>
<!-- 5. Service -->
<bean id="accountService" class="com.qcby.service.AccountServiceImpl">
<property name="accountDao" ref="accountDao"/>
</bean>
<!-- 6. Dao(第二种写法,注入 dataSource) -->
<bean id="accountDao" class="com.qcby.dao.AccountDaoImpl">
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
1.3 编写Account实体类
java
public class Account {
private Integer id;
private String name;
private Double money;
// getter、setter、toString
}
1.4 编写AccountDao接口
java
public interface AccountDao {
void outMoney(String out, double money);
void inMoney(String in, double money);
}
1.5 编写AccountDaoImpl实现类
java
import org.springframework.jdbc.core.support.JdbcDaoSupport;
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
@Override
public void outMoney(String out, double money) {
String sql = "update account set money=money-? where name=?";
getJdbcTemplate().update(sql, money, out);
}
@Override
public void inMoney(String in, double money) {
String sql = "update account set money=money+? where name=?";
getJdbcTemplate().update(sql, money, in);
}
}
1.6 编写AccountService接口
java
public interface AccountService {
void pay(String out, String in, double money);
}
1.7 编写AccountServiceImpl实现类
java
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void pay(String out, String in, double money) {
// 扣钱
accountDao.outMoney(out, money);
int i = 1 / 0;
// 加钱
accountDao.inMoney(in, money);
}
}
1.8 编写TransactionTest测试类
java
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TransactionTest {
@Autowired
private AccountService accountService;
@Test
public void testPay() {
// 熊大 → 熊二 转 100
accountService.pay("熊大", "熊二", 100);
}
}
2. 半注解的方式
2.1 编写xml配置文件
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: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/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 1. 包扫描 -->
<context:component-scan base-package="com.qcby"/>
<!-- 2. 数据源 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///spring_db"/>
<property name="username" value="root"/>
<property name="password" value="2020"/>
</bean>
<!-- 3. 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 4. 开启事务注解支持 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
2.2 Account实体类
2.3 AccountDao
2.4 AccountDaoImpl
java
import javax.sql.DataSource;
@Repository
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
@Autowired
public void setDs(DataSource dataSource) {
setDataSource(dataSource);
}
@Override
public void outMoney(String out, double money) {
String sql = "update account set money=money-? where name=?";
getJdbcTemplate().update(sql, money, out);
}
@Override
public void inMoney(String in, double money) {
String sql = "update account set money=money+? where name=?";
getJdbcTemplate().update(sql, money, in);
}
}
2.5 AccountService
2.6 AccountServiceImpl
java
@Service
@Transactional
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
@Override
public void pay(String out, String in, double money) {
accountDao.outMoney(out, money);
// 异常
int i = 1 / 0;
accountDao.inMoney(in, money);
}
}
2.7 Test类
java
import com.qcby.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TransactionTest {
@Autowired
private AccountService accountService;
@Test
public void testPay() {
// 熊大 → 熊二 转 100
accountService.pay("张三", "李四", 100);
}
}
3. 全注解的模式
3.1 编写配置类SpringConfig
java
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
@Configuration
@ComponentScan("com.qcby") // 扫描包
@EnableTransactionManagement // 开启事务!!!最重要
public class SpringConfig {
// 1. 数据源
@Bean
public DataSource dataSource() {
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql:///spring_db");
ds.setUsername("root");
ds.setPassword("2020");
return ds;
}
// 2. 事务管理器
@Bean
public DataSourceTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
3.2 AccountDao
3.3 AccountDaoImpl
java
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.stereotype.Repository;
import javax.annotation.Resource;
import javax.sql.DataSource;
@Repository
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
// 注入数据源
@Resource
public void setDs(DataSource dataSource) {
setDataSource(dataSource);
}
@Override
public void outMoney(String out, double money) {
String sql = "update account set money=money-? where name=?";
getJdbcTemplate().update(sql, money, out);
}
@Override
public void inMoney(String in, double money) {
String sql = "update account set money=money+? where name=?";
getJdbcTemplate().update(sql, money, in);
}
}
3.4 AccountService
3.5 AccountServiceImpl
java
import com.qcby.dao.AccountDao;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
@Service
@Transactional // 开启事务!!!
public class AccountServiceImpl implements AccountService {
@Resource
private AccountDao accountDao;
@Override
public void pay(String out, String in, double money) {
// 扣钱
accountDao.outMoney(out, money);
// 模拟异常 → 事务自动回滚
int i = 1 / 0;
// 加钱
accountDao.inMoney(in, money);
}
}
3.6 Test测试类
java
import com.qcby.config.SpringConfig;
import com.qcby.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class) // 加载配置类
public class TxTest {
@Resource
private AccountService accountService;
@Test
public void testPay() {
accountService.pay("小王", "小洋", 100);
}
}
二、Spring事务传播行为
Spring事务一共有七种传播行为:
1. 核心概念
当前事务:外层方法已有事务
无事务:外层没开事务传播行为
2. 七种传播行为
2.1 REQUIRED(默认)
2.1.1 特点
外层有事务->加入当前事务
外层无事务->新建一个事务场景:90%普通业务都用这个
2.2 REQUIRES_NEW
2.2.1 特点
·不管外层有没有事务,都新建一个独立事务
·外层事务挂起,互不影响
·内层回滚不影响外层,外层回滚也不影响内层
2.2.2 场景
日志记录、异步操作、一定要独立提交的业务
2.3 SUPPORTS
2.3.1 特点
外层有事务:加入
外层无事务:以非事务运行
2.3.2 场景
纯查询方法,有无事务都行
2.4 MANDATORY
2.4.1 特点
外层有事务:加入
外层无事务:直接抛异常
2.4.2 场景
强制必须在事务内运行,不能单独调用
2.5 NOT_SUPPORTED
2.5.1 特点
不管外层有没有事务,都以非事务方式运行
外层事务会被挂起
2.5.2 场景
不需要事务的耗时查询、统计
2.6 NEVER
2.6.1 特点
外层无事务:正常运行
外层有事务:直接抛异常
2.6.2 场景
强制不能在事务里执行的方法
2.7 NESTED(嵌套事务)
2.7.1 特点
外层有事务:
嵌套在当前事务里,有保存点savepoint
内层可以单独回滚,不影响外层
外层回滚,内层必须一起回滚
外层无事务:
自己新建事务
2.7.2 注意
只支持MySQL、不支持部分JTA分布式事务
3. 七种事务传播行为总结
| 传播行为 | 外层有事务 | 外层无事务 |
|---|---|---|
| REQUIRED | 加入 | 新建 |
| REQUIRES_NEW | 新建独立事务 | 新建 |
| SUPPORTS | 加入 | 非事务 |
| MANDATORY | 加入 | 抛异常 |
| NOT_SUPPORTED | 非事务运行 | 非事务 |
| NEVER | 抛异常 | 非事务 |
| NESTED | 嵌套保存点 | 新建 |
·一共七种传播行为
·默认REQUIRED
·REQUIRES_NEW是独立事务、互不干扰
·NESTED 是嵌套保存点,内层能单独回滚
·SUPPORTS/MANDATORY/NEVER/NOT_SUPPORTED多用于查询、强制事务校验。