【二十】Spring Boot 之 事务(声明式、编程式、自定义事务管理器、@EnableAspectJAutoProxy 同类方法调用)

一、简介

Spring Boot 提供声明式的事务管理机制,使用@Transactional注解。

Spring Boot 也提供编程式事务管理机制。

Spring Boot 使用事务非常简单,首先在启动类使用注解 @EnableTransactionManagement 开启事务支持后,然后在访问数据库的Service方法上添加注解 @Transactional 便可。如果注解在类上,则整个类的所有方法都默认支持事务。

事务管理器,不管是JPA还是JDBC等都实现自接口 PlatformTransactionManager 如果添加的是 spring-boot-starter-jdbc 依赖,框架会默认注入 DataSourceTransactionManager 实例。如果添加的是 spring-boot-starter-data-jpa 依赖,框架会默认注入 JpaTransactionManager 实例。

Spring内置事务管理器:

事务管理器使用场景
DataSourceTransactionManager数据源事务管理器,提供对单个javax.sql.DataSource事务管理,用于Spring JDBC抽象框架、iBATIS或MyBatis框架的事务管理
JdoTransactionManager提供对单个javax.jdo.PersistenceManagerFactory事务管理,用于集成JDO框架时的事务管理
JpaTransactionManager提供对单个javax.persistence.EntityManagerFactory事务支持,用于集成JPA实现框架时的事务管理
HibernateTransactionManager提供对单个org.hibernate.SessionFactory事务支持,用于集成Hibernate框架时的事务管理;该事务管理器只支持Hibernate3+版本,且Spring3.0+版本只支持Hibernate 3.2+版本
JtaTransactionManager提供对分布式事务管理的支持,并将事务管理委托给Java EE应用服务器事务管理器

二、@Transactional注解的参数介绍:

属性名说明
value

指定使用哪个事务管理器

如果Spring容器中存在多个 PlatformTransactionManager事务管理器 实例,

并且没有实现接口 TransactionManagementConfigurer 指定默认值,

则在方法上使用注解 @Transactional 的时候,就必须要用value指定,如果不指定,则会抛出异常。
 

示例:

@Transactional(value="txManager1")

propagation

事务传播行为

1.REQUIRED:支持事务。如果业务方法执行时已经在一个事务中,则加入当前事务,否则重新开启一个事务。默认事务传播行为

 

内层事务结束,等着外层一起提交。

外层回滚,内层也回滚。

内层回滚,外层也回滚,即使外层有try-catch,该事务也会回滚。

 

2.REQUIRES_NEW支持事务。每次都是创建一个新事物,如果当前已经在事务中了,会挂起当前事务。

 

内层事务结束,内层就提交了,不用等着外层一起提交。

外层报错回滚,不影响内层。

内层报错回滚,外层try-catch内层的异常,外层不会回滚。

内层报错回滚,外层没有捕获处理异常,外层也回滚。

 

3.NESTED支持事务。如果当前已经在一个事务中了,则嵌套在已有的事务中作为一个子事务。如果当前没在事务中则开启一个事务。

 

内层事务结束,等着外层一起提交。

外层回滚,内层也回滚。

内层回滚,外层try-catch内层的异常,不影响外层。

内层报错回滚,外层没有捕获处理异常,外层也回滚。

 

使用前提:

JDK版本要在1.4以上,有java.sql.Savepoint。

事务管理器的nestedTransactionAllowed属性为true。

外层try-catch内层的异常。

 

4.SUPPORTS支持事务。如果业务方法执行时已经在一个事务中就加入当前事务。否则就算了,不会开启一个事物。

5.MANDATORY支持事务,如果业务方法执行时已经在一个事务中,则加入当前事务。否则抛出异常。

 

6.NOT_SUPPORTED不支持事务,如果业务方法执行时已经在一个事务中,则挂起当前事务,等方法执行完毕后,事务恢复进行。

7.NEVER不支持事务。如果当前已经在一个事务中了,抛出异常。

 

示例:

@Transactional(propagation = Propagation.REQUIRED)

isolation

事务隔离级别

1.DEFAULT ,这是默认的隔离级别,使用数据库默认的事务隔离级别.另外四个与JDBC的隔离级别相对应.

2.READ_UNCOMMITTED 这是事务最低的隔离级别,它充许别外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻读。

3.READ_COMMITTED 保证一个事务修改的数据提交后才能被另外一个事务读取。这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻读。

4.REPEATABLE_READ这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻读。

5.SERIALIZABLE 事务被处理为顺序执行。防止脏读,不可重复读,防止幻读。

示例

@Transactional(isolation=Isolation.REPEATABLE_READ)

Mysql innodb默认提供的是REPEATABLE_READ

readOnly

读写或只读事务,默认读写。

示例

@Transactional(readOnly=true)

timeout

事务超时时间

示例

@Transactional(timeout=10)

单位秒

rollbackFor

导致事务回滚的异常类数组。必须继承自Throwable。

示例

@Transactional(rollbackFor=Exception.class)

rollbackForClassName导致事务回滚的异常类名字数组。必须继承自Throwable
noRollbackFor不会导致事务回滚的异常类数组。必须继承自Throwable
noRollbackForClassName

不会导致事务回滚的异常类名字数组。必须继承自Throwable

关于事务的隔离机制和传播机制,详情请看另一篇博客

【十六】Spring Boot之事务(事务传播机制、嵌套事务、事务隔离机制详解)

三、自定义多个事务管理,和指定默认事务管理器的代码示例

package com.sid.configuration;

import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.TransactionManagementConfigurer;

import javax.annotation.Resource;
import javax.sql.DataSource;

/**
 * @program: springboot
 * @description: 自定义多个事务管理器并且指定默认事务管理器
 * @author: Sid
 * @date: 2018-11-21 15:50
 * @since: 1.0
 **/
//实现TransactionManagementConfigurer接口指定默认事务管理器
@Component
public class TransactionManagerConfig implements TransactionManagementConfigurer {

    @Resource(name="txManager1")
    private PlatformTransactionManager txManager1;

    // 创建事务管理器1
    @Bean(name = "txManager1")
    public PlatformTransactionManager txManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }


    /**     没有引用spring-boot-starter-data-jpa 依赖
    // 创建事务管理器2
    @Bean(name = "txManager2")
    public PlatformTransactionManager txManager2(EntityManagerFactory factory) {
        return new JpaTransactionManager(factory);
    }
     */


    //其返回值代表在拥有多个事务管理器的情况下默认使用的事务管理
    @Override
    public PlatformTransactionManager annotationDrivenTransactionManager() {
        return txManager1;
    }
}

 使用

    @Transactional(value="txManager1")
    @Override
    public User addUser(User user) {
        
        userMapper.insert(user);
        int i = 1 / 0;
        return user;
    }

四、用@EnableAspectJAutoProxy (exposeProxy = true)方式

同一个类中有两个方法,A和B

B是使用@Transactional事务注解,在外部调用B方法的时候事务会生效

但是如果A方法中调用B方法,B的事务不会生效。

因为A方法调同一个类的B方法时,没有走代理。

解决这个问题就用到@EnableAspectJAutoProxy (exposeProxy = true)注解用来支持aop方式的自动事务配置

AOP切入同类调用方法-AopContext.currentProxy()。

代码示例:

    @Override
    public User  addUser(User user) {
        UserService proxy = (UserService) AopContext.currentProxy();
        proxy.addUserB(user);
        return user;
    }
    @Transactional(value="txManager1")
    public void addUserB(User user) {
        userMapper.insert(user);
        int i = 1 / 0;

    }

启动类

@SpringBootApplication
@EnableAspectJAutoProxy(exposeProxy = true)
@MapperScan("com.sid.mapper")//将项目中对应的mapper类的路径加进来就可以了
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }
}

五、编程式事务管理机制

不用@Transactional注解

得到事务管理器(代码参见上述:三、自定义多个事务管理,和指定默认事务管理器的代码示例)

用事务管理器来开启、提交、回滚事务

    
    // 得到事务管理器
    @Resource(name="txManager1")
    private PlatformTransactionManager txManager1;
    
    public void addUserA(User user) {
        DefaultTransactionDefinition transDefinition = new DefaultTransactionDefinition();
        //设置事务的传播机制
        transDefinition.setPropagationBehavior(DefaultTransactionDefinition.PROPAGATION_REQUIRES_NEW);
        //得到事务。根据事务的传播机制判断是新建事务还是用当前已有的事务
        TransactionStatus transStatus = txManager1.getTransaction(transDefinition);
        try {
            userMapper.insert(user);

            // 提交事务
            txManager1.commit(transStatus);
        } catch (Exception e) {

            // 回滚事务
            txManager1.rollback(transStatus);
        }
    }
Spring框架提供了两种事务管理方编程事务管理和声明事务管理。 1. 编程事务管理: 编程事务管理是通过编写代码来管理事务的提交和回滚。在这种方下,开发人员需要手动编写事务的开始、提交和回滚的代码。Spring提供了`TransactionTemplate`和`TransactionDefinition`等类来简化编程事务管理的操作。通过使用`TransactionTemplate`,可以在需要进行事务处理的代码块中对事务进行管理。 2. 声明事务管理: 声明事务管理是通过配置的方来管理事务的提交和回滚,而不需要手动编写事务管理的代码。在这种方下,开发人员只需要在需要进行事务处理的方法上使用注解或者XML配置文件来声明事务的属性,Spring框架就会根据配置自动实现事务管理。常见的注解方是使用`@Transactional`注解。 两种事务管理方各有优劣,编程事务管理灵活性较高,适用于复杂的事务场景,但需要开发人员手动编写大量的事务管理代码;声明事务管理简化了开发工作,通过配置即可实现事务管理,但对于一些复杂的业务场景可能不够灵活。 总的来说,对于大部分应用场景而言,推荐使用声明事务管理,可以减少重复代码的编写,提高开发效率。而在一些特殊的业务场景下,如需要动态控制事务的提交和回滚,或者需要手动处理一些特殊情况,可以考虑使用编程事务管理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值