目录
1.声明式事务
1.1.概述
1.2.使用
1.2.1.建表
1.2.2.maven依赖
1.2.3.配置
1.2.4.业务
1.2.5.测试
2.事务的传播行为
1.声明式事务
1.1.概述
spring中事务分为两种:
1.编程式事务,通过写代码来实现,每一步。
2.声明式事务,直接通过配置来实现。
常用的是声明式事务,接下来介绍的也是声明式事务。
1.2.使用
1.2.1.建表
一个用户表,有id和账户余额两个字段。
CREATE TABLE user (
id INT(255) AUTO_INCREMENT PRIMARY KEY,
balance INT(255)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into user VALUES(NULL,100),(NULL,100);
1.2.2.maven依赖
<properties> <spring.version>5.2.0.RELEASE</spring.version> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> <!-- 数据库连接池 --> <dependency> <groupId>com.mchange</groupId> <artifactId>c3p0</artifactId> <version>0.9.5.5</version> </dependency> <!-- 数据库驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.18</version> </dependency> </dependencies>
1.2.3.配置
尤其要注意命名空间不要少引入!!!
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx" 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/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 数据源配置 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver" />
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/springtest?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC" />
<property name="user" value="root" />
<property name="password" value="admin" />
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg ref="dataSource" />
</bean>
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!--开启注解扫描-->
<context:component-scan base-package="com.eryi.service"></context:component-scan>
<!-- 启用事务注解 -->
<tx:annotation-driven transaction-manager="transactionManager" />
</beans>
1.2.4.业务
这里模拟id为1的用户向id为2的用户转账100块,转账有两步组成:
- id为1的用户账户扣100块
- id为2的用户账户增加100块
在两步中模拟一个异常,导致只有只完成了id为1的用户扣100块的SQL执行了,但是id为2的用户增加100块的SQL没有执行。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
@Component
public class UserService {
@Autowired
private JdbcTemplate jdbcTemplate;
@Transactional
public void transfer(){
try {
// 从 fromUserId 账户中扣除金额
jdbcTemplate.update("update user set balance = balance - ? where id = ?", 100, 1);
// 抛出异常,测试事务是否回滚
int i = 1 / 0;
// 将金额转入 toUserId 账户中
jdbcTemplate.update("update user set balance = balance + ? where id = ?", 100, 2);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
1.2.5.测试
public static void main(String[] args) {
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = applicationContext.getBean("userService", UserService.class);
userService.transfer();
}
如果没有事务的话,会是这种情况:
有了事务的话,事务中所有SQL会具有原子性,要么全部成功,要么全部失败,会是这个结果:
2.事务的传播行为
Spring中存在着一个概念——事务的传播行为。事务的传播行为指的是一个事务方法被另一个事务方法调用,即嵌套事务,出现异常该怎么回滚。
Spring规定有7种事务的传播行为:
required | 如果出现异常的方法有事务就用这个事务,没有的话再去用其他的事务。例如:方法A调用方法B,如果B出现异常,B有事务就用B的事务,B没有事务,才会去用A的事务(只要有一个回滚,整体就会回滚) |
requires_new | 如果当前有事务,就用当前事务,否则就重新开一个事务。例如:方法A调用方法B,如果B出现异常,B有事务就用B的事务,B没有事务,就会去新建一个事务,最后效果是B会回滚,但是A不会回滚。 |
supports | 当前没有事务,就以非事务运行。当前有事务呢?就以当前事务运行。 |
mandatory | 其他没有事务就会抛异常。当前没有事务抛出异常,当前有事务则支持当前事务。 |
not_supported | 以非事务执行。 |
never | 以非事务执行,如果存在事务,抛出异常。 |
nested | 如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立于当前事务进行单独地提交或回滚。 |
这里附上一篇其它博主对于七种事务的传播属性的介绍,其中有详细的代码示例和讲解,很清晰:
Spring事务传播行为_荠菜煎包的博客-CSDN博客