Spring事务以及事务的传播机制

事务

在Sping中,事务有两种方式来实现,一种是通过手动编程的方式来实现,一种是通过注解的方式来进行实现.

手动编程实现事务

  @Autowired
    private DataSourceTransactionManager dataSourceTransactionManager;
    @Autowired
    TransactionDefinition transactionDefinition;
    @Autowired
    private UserService userService;
    @RequestMapping("/registry")
    public String registry(String name,String password){
        //开启事务
        TransactionStatus transactionStatus = dataSourceTransactionManager.
                getTransaction(transactionDefinition);

        userService.registeryUser(name,password);
        //提交事务
        dataSourceTransactionManager.commit(transactionStatus);
        return "注册成功";
    }

首先需要对DataSourceTransactionManager事务管理器进行定义,并注入Autowired,

@Autowired
    private DataSourceTransactionManager dataSourceTransactionManager;
    @Autowired
    TransactionDefinition transactionDefinition;

对TransactionDefinition事务定义进行注入,在需要实现事务的方法里面开启事务

事务开启:

 //开启事务
        TransactionStatus transactionStatus = dataSourceTransactionManager.
                getTransaction(transactionDefinition);

事务提交

  //提交事务
        dataSourceTransactionManager.commit(transactionStatus);

控制台事务提交显示

接下来演示事务进行回滚时的操作

//        事务进行回滚
        log.info("事务进行回滚中");
        dataSourceTransactionManager.rollback(transactionStatus);

运行结果:

可以通过观察发现,回滚后的日志没有出现事务commit

发送增加数据请求之前数据库:

发送增加数据请求之后数据库:

可以观察到数据库并没有执行数据的插入操作,可见事务回滚成功,并未进行提交,

使用注解实现事务

使用注解的时候,加在方法之前即可

@Transactinoal可以对方法或者类进行修饰

对方法进行修饰的时候,只对public修饰的方法生效,对其他方法不生效,也不报错,但是不推荐

对类进行修饰的时候,则默认对该类所有的public修饰的方法生效

public class TransactionalController {
    @Autowired
    private UserService userService;
    @RequestMapping("/registry")
    @Transactional
    public String registry(String name,String password){
        userService.registeryUser(name,password);
        return "注册成功";
    }
}

 在程序正常进行无异常的时候,事务会进行正常提交,这一部分就不演示了,

接下来演示出现异常事务是否会进行回滚

代码:

   @Transactional
    public String registry(String name,String password){
        userService.registeryUser(name,password);
        int a=10/0;
        return "注册成功";
    }

 可以看到事务进行回滚操作了,

可以看到数据库也是没有进行任何变化

  public String registry(String name,String password){
        userService.registeryUser(name,password);
        try{
            int a=10/0;
        }
        catch (Exception e){
            e.printStackTrace();
        }

        return "注册成功";
    }

 

可以看到虽然程序运行时出错了,但是事务提交了,是因为程序运行时抛出的异常被捕获到了,进行处理之后,@Transactional注解会自动进行提交,不会进行回滚,如果还想进行回滚需要手动进行设置,

  try{
            int a=10/0;
        }
        catch (Exception e){
//            e.printStackTrace();
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }

 可以通过这样设置,来手动进行事务的回滚

通过观察日志,我们可以发现事务并没有进行commit,而是进行了rollback.

  @RequestMapping("/r2")
    @Transactional
    public String registry2(String name,String password) throws IOException {
        userService.registeryUser(name,password);
  if(true){
      throw new IOException();
  }
        return "注册成功";
    }

这次我们让程序主动抛出异常, 

可以发现,虽然主动抛出了异常,但是事务还是进行了提交

只有当异常为RuntimeException以及他的子类和error时才会进行回滚,其他异常则不会进行回滚

@Transactional(rollbackFor = Exception.class)

在将注解改成这样之后,将rollbackFor指定异常为Exception之后,该事务才会进行回滚.

事务的特性

rollbackFor:能指定事务回滚时的异常类型,可以指定多个异常类型

Isolation:事务的隔离级别,默认值为Isolation.DEFAULT

propagation:事务的传播机制,默认值为propagation.REQUIRED

1.Isolation.DEFAULT : 以连接的数据库的事务隔离级别为主.

2. Isolation.READ_UNCOMMITTED : 读未提交, 对应SQL标准中 READ UNCOMMITTED

3. Isolation.READ_COMMITTED : 读已提交,对应SQL标准中 READ COMMITTED

4. Isolation.REPEATABLE_READ : 可重复读, 对应SQL标准中 REPEATABLE READ

5. Isolation.SERIALIZABLE : 串⾏化, 对应SQL标准中 SERIALIZABLE

事务的传播机制

什么叫事务传播,当A方法和B方法同时被注解@Transactional进行修饰的时候,并且在A方法中调用B方法,那么A方法的事务和B方法的事务该如何开启呢,我们把这种现象称为事务的传播机制.

事务的传播机制分类

1.Propagation.REQUIRED    默认的事务传播机制,如果当前存在事务,则加入该事务,如果当前没有事务,则创建一个新的事务

2.Propagation.MANDATORY 强制性.如果当前存在事务,则加入该事物,如果当前没有事务,则抛出异常

3.Propagation.SUPPORTS:如果当前存在事务,则加入该事务,如果当前没有事务,则以非事务的方式继续运行

4.Propagation.REQUIRES_NEW:创建一个新的事务.如果当前存在事务,则把当前事务挂起,也就是说不管外部方法是否开启事务,Propagation.REQUIRES_NEW所修饰的内部方法都会开启一个新的事务

5.Propagation.NOT_SUPPORTED:以非事务的方式进行运行,如果当前存在事务,则把当前事务进行挂起,

6.Propagation.NEVER:以非事务的方式运行,如果有当前事务,则抛出异常

7.Propagation.NESTED:如果当前存在事务,则创建一个当前事务的嵌套事务,如果当前没有事务,则创建一个事务

conttroller类:

@RequestMapping("/test")
public class UserController2 {
    @Autowired
    private UserService userService;
    @Autowired
    private LogService logService;
    @RequestMapping("/u1")
    @Transactional
    public String registry(String name,String password){

        userService.registeryUser(name,password);
        logService.inserLog(name,"注册成功");
        return "注册成功";
    }

 LogService类:

@Service
public class LogService {
    @Autowired
    private LogInfoMapper logInfoMapper;
    @Transactional(propagation =Propagation.REQUIRED)
    public void inserLog(String name,String op){
        logInfoMapper.insertLog(name,"用户注册");
        int a=10/0;
    }
}

Userservice类: 

@Service
public class UserService {
    @Autowired
    private UserInfoMapper userInfoMapper;
    @Transactional(propagation = Propagation.REQUIRED)
    public void registeryUser(String name,String password){
        userInfoMapper.insert(name,password);
    }
}

可以发现两次操作是在同一个事务开启的,并且由于发生了异常进行回滚,进行的两次插入操作均失效,

 

  @Transactional(propagation =Propagation.REQUIRES_NEW)

将事务的传播机制改为Propagation.REQUIRES_NEW之后 

可以发现开启了两个事务,其中第二个事务由于发生异常进行了回滚操作,第一个事务成功提交了,说明这两个事务的隔离级别设置为Propagation.REQUIRES_NEW时,事务之间是相互不干扰的. 

    @Transactional(propagation =Propagation.NEVER)

在将事务的隔离级别改为NEVER的时候,程序直接抛出异常

 

@Transactional(propagation =Propagation.NESTED)

 

在将隔离级别改为NESTED的时候,可以发现,在其中的一个事务发生异常的时候,两个操作都会失效,都会进行回滚.

logInfoMapper.insertLog(name,"用户注册");
       try{ int a=10/0;}
       catch (Exception e){
           TransactionAspectSupport.
currentTransactionStatus().setRollbackOnly();
       }

 

http://127.0.0.1:8080/test/u1?name=123&password=1234

url请求访问之前数据库:

url请求访问之后数据库:

通过观察,我们可以发现只有log_info表的插入数据进行了回滚,然而user_info表中的插入数据并没有进行回滚,这是nested和required的区别.在于nested是一个嵌套事务,只要其中的一个事务把自己的异常处理好了并且即使rollback了也不会影响另外一个事务的正常提交.然而required由于是同一个事务,所以在一个事务中进行rollback了,另外一个也会自动进行rollback

mybatis运行日志

nested和required的区别

整个事务如果执行成功,那么两种级别是一样的.

如果其中的一个事务发生了异常,那么这个事务在nested中是可以进行局部回滚的,而required是做不到的.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值