Spring——声明式事务

Spring的声明式事务使用起来比较简单,只要在方法的上方添加@Transactional注解,就将一个方法变成事务方法了。使用声明式事务,可以将事务管理代码从业务方法中抽离出来(也就是不用在方法里写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(); //该方法的注解@Transactional(propagation = Propagation.REQUIRED)
        bookService.updatePrice(BOOK_ID); //该方法的注解@Transactional(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(); //该方法的注解@Transactional(propagation = Propagation.REQUIRES_NEW)
        bookService.updatePrice(BOOK_ID); //该方法的注解@Transactional(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(); //该方法的注解@Transactional(propagation = Propagation.SUPPORTS)
 }

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

NOT_SUPPORTED
 @Transactional()
 public void multi(){
        bookService.buyBook(); //该方法的注解@Transactional(propagation = Propagation.NOT_SUPPORTED)
 }

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

MANDATORY
 //@Transactional()
 public void multi(){
        bookService.buyBook(); //该方法的注解@Transactional(propagation = Propagation.NEVER)
 }

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

NEVER
 @Transactional()
 public void multi(){
        bookService.buyBook(); //该方法的注解@Transactional(propagation = Propagation.NEVER)
 }

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

NESTED
    @Transactional(rollbackFor = {java.lang.ClassCastException.class})
    public void multi(){
        try {
            bookService.buyBook();//该方法的注解@Transactional(propagation = Propagation.NESTED)
        }catch(Exception e){
        }
        bookService.updatePrice(BOOK_ID);//该方法的注解@Transactional
        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:不论被捆绑的事务在外部事务中做了怎样的异常捕获,均被外部事务中的异常所制约,一旦外部事务发生异常,所有捆绑到外部事务中的事务,都要回滚。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值