Spring——声明式事务详解编程语言

Spring的声明式事务使用起来比较简单,[email protected],就将一个方法变成事务方法了。使用声明式事务,可以将事务管理代码从业务方法中抽离出来(也就是不用在方法里写commit和rollback),以声明的方式来实现事务管理。

Spring的核心事务管理抽象是PlatformTransactionManager,要想使用声明式事务,必须先在xml中先将其声明为bean,而且还要记得配置数据源

 <!--这么写就可以扫描到db.properties的配置信息--> 
    <context:property-placeholder location="classpath:db.properties"></context:property-placeholder> 
 <!--配置数据源--> 
    <bean id = "dataSource" class = "com.alibaba.druid.pool.DruidDataSource"> 
        <property name="username" value="${jdbc.username}"></property> 
        <property name="password" value="${jdbc.password}"></property> 
        <property name="url" value="${jdbc.url}"></property> 
        <property name="driverClassName" value="${jdbc.driverName}"></property> 
    </bean> 
    <!--配置事务管理器的bean对象--> 
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
        <property name="dataSource" ref="dataSource"></property> 
    </bean> 
 
	<!--如果代码中用到jdbc操作数据库,可以加下面的配置--> 
	<!--将JdbcTemplate注册为bean--> 
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> 
        <property name="dataSource" ref="dataSource"></property> 
    </bean> 
     
    <!--开启事务管理器的配置--> 
    <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven> 
#db.properties 
jdbc.username = root 
jdbc.password = root 
jdbc.url = jdbc:mysql://localhost:3306/tx 
jdbc.driverName = com.mysql.jdbc.Driver 

@Transactional

下面说明一下Transactional里的重要参数

  • ioslation:隔离级别。可配置为可重复读,读已提交,读未提交,序列化,DEFAULT(这个配置将使用选择的数据库的默认事务隔离级别)

  • timeout:超时时间。若方法的执行时间超过设定值,则报超时异常,单位是秒

  • readOnly:只读,表示该方法不允许修改数据

  • rollbackFor:指定一个或多个异常,当方法发生这个异常时,回滚
    @Transactional(rollbackFor = {java.lang.ArithmeticException.class})。这一项,强烈建议每个声明式事务都必须配置

  • rollbackForClassName:与上同,只是传参形式不同
    @Transactional(noRollbackForClassName = "java.lang.ArithmeticException")

  • noRollbackFor:指定一个或多个异常,当方法发生这个异常时,不回滚
    @Transactional(noRollbackFor = {java.lang.ArithmeticException.class})

  • noRollbackForClassName:与上同,只是传参形式不同
    @Transactional(noRollbackForClassName = "java.lang.ArithmeticException")

  • propagation:传播特性,表示不同事务之间执行的关系,下文将重点阐述传播特性

事务的传播特性

传播特性:表示不同事务之间执行的关系。或者说当一个事务方法A被另一个事务方法B调用时,A和B会发生怎样的关系

下面的文字比较绕,如果看不懂(一般人都看不懂…)直接看下面的例子解释就行
在这里插入图片描述
注意下面所有multi方法与buyBook和updatePrice不在一个类中。若在一个类,multi对buyBook和updatePrice的调用将会是普通调用,不会产生事务传播特性

REQUIERD(这是默认传播特性)
 @Transactional() 
 public void multi(){
    
        bookService.buyBook(); [email protected](propagation = Propagation.REQUIRED) 
        bookService.updatePrice(BOOK_ID); [email protected](propagation = Propagation.REQUIRED) 
 } 

REQUIRED的效果是:当内部的事务方法配置了REQUIRED之后,事务的控制交由外部事务方法(multi)控制。也就是说,将buyBook和updatePrice统一成一个事务,只有buyBook和updatePrice都commit时,整个multi才算commit。不论buyBook还是updatePrice发生异常(不论是否在multi被try-catch捕获),二者都会rollback,一个都不会commit。如果取消multi的事务声明使之变为普通方法,那么buyBook和updatePrice,谁发生异常谁rollback,不影响另一个方法的commit

REQUIRES_NEW

 @Transactional() 
 public void multi(){
    
        bookService.buyBook(); [email protected](propagation = Propagation.REQUIRES_NEW) 
        bookService.updatePrice(BOOK_ID); [email protected](propagation = Propagation.REQUIRED) 
 } 

REQUIRES_NEW的效果是:配置成REQUIRES_NEW的方法会单独执行一个事务,若buyBook发生异常(不论是否被try-catch捕获),则buyBook会rollback,不影响updatePrice的commit。从REQUIRES_NEW这一名字就可联想到,配成REQUIRES_NEW的方法,需要一个新事务单独执行,而由于事务的隔离性,所以配成REQUIRES_NEW的事务不会影响其他事务,即使它们被捆绑到同一个事务方法(multi)里

SUPPORTS

 @Transactional() 
 public void multi(){
    
        bookService.buyBook(); [email protected](propagation = Propagation.SUPPORTS) 
 } 

SUPPORTS的效果是:如果multi是事务方法,则buyBook受事务控制。如果multi是普通方法,则buyBook不受事务控制(即,发生异常的代码前的逻辑会成功commit,发生异常代码后的逻辑会报错失败)。SUPPORTS方式几乎不用

NOT_SUPPORTED

 @Transactional() 
 public void multi(){
    
        bookService.buyBook(); [email protected](propagation = Propagation.NOT_SUPPORTED) 
 } 

NOT_SUPPORTED的效果是:不论multi是不是事务方法,buyBook均不受事务控制(即,发生异常的代码前的逻辑会成功commit,发生异常代码后的逻辑会报错失败)。NOT_SUPPORTED方式几乎不用

MANDATORY

 [email protected]() 
 public void multi(){
    
        bookService.buyBook(); [email protected](propagation = Propagation.NEVER) 
 } 

NEVER的效果是:必须在buyBook外面再套一层事务方法,否则会报下列错误。MANDATORY几乎不用(唯一的用处可能就是,强制让其他程序员在使用buyBook时,必须要与其他事务一起使用,捆绑在一个事务里)在这里插入图片描述

NEVER

 @Transactional() 
 public void multi(){
    
        bookService.buyBook(); [email protected](propagation = Propagation.NEVER) 
 } 

NEVER的效果是:不允许buyBook外面再套一层事务方法,否则会报下列错误。NEVER与MANDATORY正好相反。NEVER方式几乎不用(唯一的用处可能就是,强制让其他程序员在使用buyBook时,不允许再在外层套一个事务方法)
在这里插入图片描述

NESTED

    @Transactional(rollbackFor = {
   java.lang.ClassCastException.class}) 
    public void multi(){
    
        try {
    
            bookService.buyBook();[email protected](propagation = Propagation.NESTED) 
        }catch(Exception e){
    
        } 
        bookService.updatePrice(BOOK_ID);[email protected] 
        int i = 1/0; 
    } 

NEVER的效果是:如果buyBook方法发生异常(updatePrice无异常),不被捕获,则buyBook和updatePrice都不会回滚。可是如果buyBook被捕获了,并且multi发生异常时,则buyBook和updatePrice都会回滚。一句话说就是:NESTED的配置会导致,不论multi里怎么对异常方法处理了,所有方法均会回滚。换言之,multi里的所有方法,都被multi里都异常所制约

对REQUIRED,REQUIRES_NEW,NESTED做一个区别

  • REQUIRED:将所有事务方法捆绑为一个事务,一荣俱荣,一殒具殒
  • REQUIRES_NEW:单独将配置成REQUIRES_NEW的那个事务视为独立的事务,不管它有没有被捆绑到外部事务中,不管外部事务是否发生异常,它的成功commit与否,均与其他事务无关。
  • NESTED:不论被捆绑的事务在外部事务中做了怎样的异常捕获,均被外部事务中的异常所制约,一旦外部事务发生异常,所有捆绑到外部事务中的事务,都要回滚。

原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/tech/pnotes/20577.html

(0)
上一篇 2021年7月19日 23:34
下一篇 2021年7月19日 23:34

相关推荐

发表回复

登录后才能评论