【二十】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,boot)