一、前言
本篇文章主要内容: Springboot中事务的简单使用, 事务的几种传播方式, 常见的一种事务失效场景及其解决方式
Spring相关文章汇总(Ps:有部分还未完成):
- Spring中IOC DI和AOP分别是什么
- Spring底层核心原理解析
- Bean的声明周期底层原理
- 依赖注入底层原理
- 初始化底层原理
- 构造方法底层原理
- AOP底层原理
二、Spring中事务的几种传播行为
事务的传播行为: 当多个声明的事务方法在相互调用时, 这个事务的传递方式
Spring的七种事务传播行为:
- REQUIRED(默认): 他是Spring里面默认的事务传播行为, 如果当前存在事务就加入, 不存在则新增
- REQUIRED_NEW: 他不管当前是否存在事务, 都会新增一个事务来执行, 新老事务相互独立, 外部事务抛出异常不影响内部事务
- NESTED: 如果当前存在事务则以嵌套事务执行, 不存在则新增
- SUPPORTS: 表示支持当前的事务, 存在则加入, 不存在就以非事务的方式执行
- NOT_SUPPORT: 以非事务的方式来执行, 若当前存在事务, 则把当前事务挂起
- MANDATORY: 强制的事务执行, 若当前不存在事务则抛出异常
- NEVER: 以非事务的方式来执行, 若当前存在事务则抛出异常
三、事务
Spring事务的代理对象执行某个方法时的步骤
- 判断有没有@Transctional注解
- 若存在@Transctional注解, 则创建一个数据库连接conn (事务管理器 DataSource)
- 修改属性: conn.autocommit = false
- 执行业务代码
- 执行完了没有异常则提交, 若存在异常则回滚
Spring事务是否会失效的判断标准:
某个加了 @Transaction注解的方法被调用时, 要判断是否是直接被代理对象调用的, 如果是则事务会生效, 若不是则失效, 具体测试可看下面常见事务失效场景案例
事务的简单应用
新建test库juejin表
Config类为
@ComponentScan("com.ningxuan") @Configuration public class AppConfig { @Bean public NingxuanService ningxuanService(){ return new NingxuanService(); } @Bean // jdbc public JdbcTemplate jdbcTemplate(){ return new JdbcTemplate(dataSource()); } @Bean // 事务管理器 public PlatformTransactionManager transactionManager(){ DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(); transactionManager.setDataSource(dataSource()); return transactionManager; } public DataSource dataSource(){ DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setUrl("jdbc:mysql://127.0.0.1/test?useUnicode=true&characterEncoding=UTF-8&useSSL=false"); dataSource.setUsername("username"); dataSource.setPassword("password"); return dataSource; } } 复制代码
业务类为
@Component() public class NingxuanService implements InitializingBean { @Resource private JdbcTemplate jdbcTemplate; // 使用事务 @Transactional public void test() { jdbcTemplate.execute("1, 'ningxuan', 'https://juejin.cn/user/3334188415845838'"); // 抛出一个空指针异常 throw new NullPointerException(); } } 复制代码
启动main方法为
public static void main(String[] args) { // 创建一个Spring对象 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); // 获取Bean容器 NingxuanService ningxuanService = (NingxuanService)context.getBean("ningxuanService"); // 执行方法 ningxuanService.test(); } 复制代码
可以看到, 我们在NingxuanService类里面对test()方法开启了事务注解@Transctional, 现在我们执行一下main方法爆了空指针异常, 同时我们的数据库新增也失败了
四、事务的常见失效场景
@Transctional失效
还是上面的Config类和main方法, 在我们的业务类NingXuanService类中新增test2()方法, 同时把test2()方法的事务传播机制设置为 NEVER
NEVER: 以非事务的方式来执行, 若当前存在事务则抛出异常
按照下面的代码来看, test()方法调用了test2()方法, 且test()方法存在事务, 那么按照事务传播方式 NEVER来看, 执行到test2()的时候就会抛出异常, 代码回滚, 具体如下
@Component() public class NingxuanService implements InitializingBean { @Resource private JdbcTemplate jdbcTemplate; @Transactional public void test() { jdbcTemplate.execute("insert juejin value(1, 'ningxuan', 'https://juejin.cn/user/3334188415845838')"); test2(); } // 设置传播机制为NEVER @Transactional(propagation = Propagation.NEVER) public void test2(){ jdbcTemplate.execute("insert juejin value(2, 'ningxuan_blog', 'https://juejin.cn/user/3334188415845838')"); } } 复制代码
执行main方法之后, 我们发现事情没有按照我们想的那样进行, 没有异常的同时, 两条SQL还都执行成功, MySQL中能看到结果了, 这是为什么呢?
它实际的原因是因为我们在执行test2()方法的时候, 不是通过代理对象去执行的, 而是通过普通对象去执行的test2()方法, 这个时候没有去走@Transctional注解
执行test2()就相当于在NingxuanService中去 this.test2(); 而不是通过代理对象去执行的
解决方式:
- 调用别的代理 新建一个类, 将我们的test2()方法放入那个类中, 然后调用
@Component public class TestService { @Transactional(propagation = Propagation.NEVER) public void test2(){ } } 复制代码
修改之后的NingxuanService, test2()通过testService去调用
@Component() public class NingxuanService implements InitializingBean { @Resource private JdbcTemplate jdbcTemplate; @Resource private TestService testService; @Transactional public void test() { jdbcTemplate.execute("insert juejin value(1, 'ningxuan', 'https://juejin.cn/user/3334188415845838')"); testService.test2(); } } 复制代码
执行main方法, 如期报错
- 自己注入自己
修改NingxuanService调用test2()方法, 改为自己调用自己的方式
@Component() public class NingxuanService implements InitializingBean { @Resource private JdbcTemplate jdbcTemplate; @Resource private NingxuanService ningxuanService; @Transactional public void test() { jdbcTemplate.execute("insert juejin value(1, 'ningxuan', 'https://juejin.cn/user/3334188415845838')"); ningxuanService.test2(); } // 设置传播机制为NEVER @Transactional(propagation = Propagation.NEVER) public void test2(){ jdbcTemplate.execute("insert juejin value(2, 'ningxuan_blog', 'https://juejin.cn/user/3334188415845838')"); } } 复制代码
执行main方法之后, 如期报错
本文内容到此结束了