Spring事务

   现如今,对于一个Java程序猿来说,如果说不会用甚至不知道Spring,那肯定不是一个合格的Java程序猿。大家都知道,Spring最主要的俩大模块,分别是IoC和AOP。而今天主要讲的是Spring中的事务@Transactional注解。

Sping事务的传播机制

名称解释
TransactionDefinition.PROPAGATION_REQUIRED如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值。
TransactionDefinition.PROPAGATION_REQUIRES_NEW创建一个新的事务,如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_SUPPORTS如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
TransactionDefinition.PROPAGATION_NOT_SUPPORTED以非事务方式运行,如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_NEVER以非事务方式运行,如果当前存在事务,则抛出异常。
TransactionDefinition.PROPAGATION_MANDATORY如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
TransactionDefinition.PROPAGATION_NESTED如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。

Spring事务简单应用

基于Spring AOP来实现Spring事务,加了注解@EnableTransactionManagement开启事务后,在类或者public方法上加上@Transactional注解就可以了。

pom.xml

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.3.10</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.3.10</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.47</version>
    </dependency>

AppConfig.java

@ComponentScan("com.nxj.dev")
@Configuration
@EnableTransactionManagement
public class AppConfig {
​
    @Bean
    public JdbcTemplate jdbcTemplate() {
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource());
        return jdbcTemplate;
    }
​
    @Bean
    public PlatformTransactionManager transactionManager() {
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource());
        // 默认是true(事务 部分失败时,全局回滚)
        // 如果是false(事务 部分失败时,不回滚,都提交)
//        transactionManager.setGlobalRollbackOnParticipationFailure(true);
        return transactionManager;
    }
​
    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://ip:3306/spring_transaction?characterEncoding=utf-8&useSSL=false");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        return dataSource;
    }
}

OrderService.java

@Component
public class OrderService {
    @Autowired
    JdbcTemplate jdbcTemplate;
​
    @Autowired
    OrderService orderService;
    
    // 1和2和3都插入失败
    @Transactional
    public void test1() {
        // 1
        jdbcTemplate.execute("insert into order_info values ('008', 8.88)");
        // 2
        jdbcTemplate.execute("insert into order_info values ('009', 9.99)");
        // 报错:java.lang.ArithmeticException: / by zero
        // Spring事务默认会在遇到RuntimeException、Error以及它们的子类的异常时,会回滚事务
        // 而ArithmeticException是RuntimeException的子类
        int i = 1/0;
        // 3
        jdbcTemplate.execute("insert into order_info values ('010', 10.00)");
    }
    
    // 1和2和3都成功
    @Transactional
    public void test2() throws SQLException {
        // 1
        jdbcTemplate.execute("insert into order_info values ('008', 8.88)");
        // 2
        jdbcTemplate.execute("insert into order_info values ('009', 9.99)");
        // 3
        jdbcTemplate.execute("insert into order_info values ('010', 10.00)");
        // Spring事务默认会在遇到RuntimeException、Error以及它们的子类的异常时,会回滚事务
        throw new SQLException();
    }    
    
    // 1和2和3都失败
    @Transactional(rollbackFor = Exception.class)
    public void test3() throws SQLException {
        // 1
        jdbcTemplate.execute("insert into order_info values ('008', 8.88)");
        // 2
        jdbcTemplate.execute("insert into order_info values ('009', 9.99)");
        // 3
        jdbcTemplate.execute("insert into order_info values ('010', 10.00)");
        // Spring事务默认会在遇到RuntimeException、Error以及它们的子类的异常时,会回滚事务
        throw new SQLException();
    }
    
    // 1和2和3都失败
    @Transactional
    public void test4() throws RuntimeException {
        // 1
        jdbcTemplate.execute("insert into order_info values ('008', 8.88)");
        // 2
        jdbcTemplate.execute("insert into order_info values ('009', 9.99)");
        // 3
        jdbcTemplate.execute("insert into order_info values ('010', 10.00)");
        // Spring事务默认会在遇到RuntimeException、Error以及它们的子类的异常时,会回滚事务
//        throw new RuntimeException();
        throw new Error();
    }    
    
    
    // 1和2都插入成功,3 失败
    public void test5() {
        a();
    }
​
    // 1和2和3 都失败
    public void test6() {
        // 在同一个类中,非事务方法调用事务方法,事务失效,得采用(Service)AopContext.currentProxy().xx()来进行调用,事务才能生效。
        ((OrderService) AopContext.currentProxy()).a();
    }
​
    // 1和2和3 都失败
    public void test7() {
        // 在同一个类中,非事务方法调用事务方法,事务失效,也可以采用自己注入自己,事务能生效。
        orderService.a();
    }
​
    @Transactional
    public void a() {
        // 1
        jdbcTemplate.execute("insert into order_info values ('008', 8.88)");
        // 2
        jdbcTemplate.execute("insert into order_info values ('009', 9.99)");
        // 报错:java.lang.ArithmeticException: / by zero
        int i = 1/0;
        // 3
        jdbcTemplate.execute("insert into order_info values ('010', 10.00)");
    }
    
    
    // 1和2和3都失败
    @Transactional
    public void test8() {
        // 1
        jdbcTemplate.execute("insert into order_info values ('008', 8.88)");
        b();
    }
​
    // 加不加@Transactional注解,都无所谓,因为同一个类中方法之间直接调用,被调用的方法上@Transactional失效
    @Transactional
    public void b() {
        // 2
        jdbcTemplate.execute("insert into order_info values ('009', 9.99)");
        int i = 1/0;
        // 3
        jdbcTemplate.execute("insert into order_info values ('010', 10.00)");
    }
    
    
    // 1和2成功,3失败
    @Transactional
    public void test9() {
        // 1
        jdbcTemplate.execute("insert into order_info values ('008', 8.88)");
        try {
            b();
        } catch (Exception e) {
​
        }
    }
​
    // 1和2和3失败
    @Transactional
    public void test10() {
        // 1
        jdbcTemplate.execute("insert into order_info values ('008', 8.88)");
        try {
            b();
        } catch (Exception e) {
            //手动强制回滚事务,然后可以抛出自定义的异常给前端
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
    }    
    
    // 1和2和3失败
    @Transactional
    public void test11() {
        // 1
        jdbcTemplate.execute("insert into order_info values ('008', 8.88)");
        try {
            orderService.b();
        } catch (Exception e) {
        }
    }   
​
    // 1和2都成功,3失败
    @Transactional
    public void test12() {
        // 1
        jdbcTemplate.execute("insert into order_info values ('008', 8.88)");
        orderService.c();
    }
​
    @Transactional
    public void c() {
        try {
            // 2
            jdbcTemplate.execute("insert into order_info values ('009', 9.99)");
            int i = 1/0;
            // 3
            jdbcTemplate.execute("insert into order_info values ('010', 10.00)");
        } catch (Exception e) {
​
        }
    }    
}

@EnableTransactionManagement

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
  /*
  * proxyTargetClass
     *  true:
     *    无论目标方法是否实现了接口,都使用CGLIB代理
     *  false:默认值
     *    如果目标方法实现了接口,使用JDK动态代理
     *    如果目标方法没有实现接口,使用CGLIB代理
     */  
    boolean proxyTargetClass() default false;
​
    AdviceMode mode() default AdviceMode.PROXY;
​
    int order() default Ordered.LOWEST_PRECEDENCE;
}

可以看到,这个注解也挺简单的,就是通过@Import注解,引入了另外一个bean,通过查看TransactionManagementConfigurationSelector的类继承关系,可以发现,这个类实现了ImportSelector注解,所以,会实现selectImports()方法,在该方法中,注入了两个重要的bean

  1. AutoProxyRegistrar

  2. ProxyTransactionManagementConfiguration

AutoProxyRegistrar

AutoProxyRegistrar实现了ImportBeanDefinitionRegistrar接口,所以,在registerBeanDefinitions方法中,注入一个bean

InfrastructureAdvisorAutoProxyCreator 可以发现,这个类是一个后置处理器,继承了InstantiationAwareBeanPostProcessor

所以:这就是@EnableTransactionManagement的第一个作用,注入了一个后置处理器,这个后置处理器就是用来对事务注解进行增强的

ProxyTransactionManagementConfiguration

该类只有一个注解@Configuration,所以该类是一个配置类,在该类中,通过三个@Bean注解,向spring容器中注入了事务执行时要用到的组件

/**
     * @return
     * 注入事务增强器
     * 这里是创建一个advisor,然后设置切点(TransactionInterceptor)和通知(TransactionAttributeSource)
     * 这里的BeanFactoryTransactionAttributeSourceAdvisor类似于aop中的advisor
     */
    @Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {
        BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
        //事务增强器会解析事务注解的参数信息
        advisor.setTransactionAttributeSource(transactionAttributeSource());
        advisor.setAdvice(transactionInterceptor());
        if (this.enableTx != null) {
            advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
        }
        return advisor;
    }
​
    /**
     * @return
     * 往spring容器中注入事务解析器(解析事务注解上的各个参数)
     * 在执行第八个后置处理器的时候,判断是否需要增强的时候,会解析transaction注解
     *
     * 这里在new AnnotationTransactionAttributeSource()对象的时候,有一个非常关键的点:
     *  publicMethodsOnly  这里在调用构造函数的时候,默认初始化该值为true;该值的意思是:只允许public方法进行事务代理
     *
     *  在后面判断是否可以对方法进行增强的时候,会判断该值,以及对应method是否是public,如果是非public修饰的方法,直接return null,不进行代理增强
     */
    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public TransactionAttributeSource transactionAttributeSource() {
        return new AnnotationTransactionAttributeSource();
    }
​
    /**
     * @return
     * 定义事务拦截器,并将事务拦截器注入到spring容器中
     */
    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public TransactionInterceptor transactionInterceptor() {
        TransactionInterceptor interceptor = new TransactionInterceptor();
        interceptor.setTransactionAttributeSource(transactionAttributeSource());
        if (this.txManager != null) {
            interceptor.setTransactionManager(this.txManager);
        }
        return interceptor;
    }

所以:该类是给spring容器中,注入了三个bean,分别是:事务拦截器、事务解析器、事务增强器

Advisor=Advice+Pointcut

其中

Advisor=>BeanFactoryTransactionAttributeSourceAdvisor

Advice=>TransactionInterceptor

Pointcut=>TransactionAttributeSource=>TransactionAttributeSourcePointcut

参考链接:spring事务源码-代理对象生成过程解析_小小少年_的博客-CSDN博客_spring 事务代理对象

参考链接:公司新来一个同事,把 @Transactional 事务注解运用得炉火纯青。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值