Spring框架的事务管理

目录

一、Spring框架的事务管理

[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测试类)

二、Spring事务传播行为

[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多用于查询、强制事务校验。

相关推荐
javachen__1 小时前
JsonView:一套VO实现多角色字段返回
java·linux·服务器
卷无止境1 小时前
如何让AI Agent老老实实遵守公司代码规范——以Pi Agent为例
后端·python
行者-全栈开发2 小时前
SpringBoot 生产环境踩坑实录:10个真实故障案例与教训
java·spring boot·后端
李福春2 小时前
斗破Google ADK Java苍穹-第7境斗皇-御天地-Harness治理
java·人工智能·python·深圳同盟·腾讯云架构师技术同盟·adk java·harness 治理
Wang's Blog2 小时前
Java 项目部署之 Docker工具快速入门: 容器命令练习:创建、改文件、重启到删除的完整闭环
java·开发语言·docker
我是人✓2 小时前
Java面向对象核心特性深度解析
java·前端·算法
JaguarJack2 小时前
免费可商用 PHP 管理后台 CatchAdmin V5.4.1 发布,新增 MCP 支持
后端·php·laravel·服务端
Wx-bishekaifayuan2 小时前
springboot线上中医药养生平台管理系统36753-计算机课程设计、毕业设计
java·spring boot·后端·spring·课程设计·express·旅游
IT_陈寒2 小时前
为什么我的Vue组件总是莫名其妙更新?
前端·人工智能·后端