Spring之事务

简介: Spring之事务

一、前言

本篇文章主要内容: Springboot中事务的简单使用, 事务的几种传播方式, 常见的一种事务失效场景及其解决方式

Spring相关文章汇总(Ps:有部分还未完成):

二、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方法之后, 如期报错

网络异常,图片无法展示
|

本文内容到此结束了



目录
相关文章
|
1月前
|
Java 数据库 开发者
|
5天前
|
XML Java 数据库
Spring框架第五章(声明式事务)
Spring框架第五章(声明式事务)
|
13天前
|
Java 开发者 Spring
spring事务类型,事务传递,隔离级别?
spring事务类型,事务传递,隔离级别?
|
3天前
|
存储 Java 关系型数据库
Spring事务失效的 8 大原因,这次可以吊打面试官了!
Spring事务失效的 8 大原因,这次可以吊打面试官了!
9 0
|
26天前
|
Java 关系型数据库 MySQL
Spring 事务和事务传播机制
Spring 事务和事务传播机制
Spring 事务和事务传播机制
|
27天前
|
Java 关系型数据库 MySQL
Spring 事务和事务传播机制
Spring 事务和事务传播机制
|
27天前
|
Java 数据库 Spring
Spring 事务
Spring 事务
27 1
|
27天前
|
Java 数据库 Spring
Spring 事务 (编程式 & 声明式, Spring 事务传播机制)
Spring 事务 (编程式 & 声明式, Spring 事务传播机制)
25 1
|
30天前
|
Java 数据库连接 Spring
Spring事务
Spring事务
24 1
|
14天前
|
Java Spring 容器
Spring 中一个事务方法调用另一个事务方法会怎样?
【6月更文挑战第5天】如果我们在一个包含事务的方法中,调用了另一个包含里事务的方法,那么,Spring 会创建新的事物吗?如果方法抛出异常导致事务会滚了呢?
24 0