Spring事务的配置与使用

Spring支持的事务

Spring提供两种事务管理方式,分为编程式和声明式。

  • 编程式:通过编码的方式手动启用、提交或回滚事务,粒度更细,但更麻烦。
  • 声明式:通过在方法或类或接口上添加注解进行包装,无侵入地实现事务,更方便,但粒度更大。

需要注意的是,使用的数据库需要支持事务,否则事务将不起作用。如MySql的MyIsam引擎就不支持事务。

Spring事务的配置

添加依赖

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>${spring.version}</version>
    </dependency>

配置事务管理器

    <bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    
    <!-- 如果使用基于注解的声明式事务,需要配置annotation-driven -->
    <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>

显然,transactionManager的dataSource应该是代码中进行事务操作的dataSource,不然怎么管理呢。

编程式事务的使用

在代码中开始、提交或回滚事务:

    // 开始事务
    TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
    // 异常回滚,并不一定要在try...catch中
    transactionManager.rollback(status);
    // 提交事务
    transactionManager.commit(status);

通常比较合适的使用方法:

    TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
    try {
        // 进行需要处在同一事务中的数据库操作
        ......
        // 正常结束,提交事务
        transactionManager.commit(status);
    } catch (SomeException) {
        // 错误处理
        ......
        // 回滚
        transactionManager.rollback(status);
    }

声明式事务的使用

直接在类或在方法上添加@Transaction注解即可:

@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, rollbackFor = Exception.class)
public int save(Info info) throws Exception {

@Transaction注解的常用参数:
propagation 传播行为

  • REQUIRED(默认值)
    必须在一个具有事务的上下文中运行:如果当前没有(外层)事务,就新建一个事务;如果当前存在(外层)事务,则加入到当前(外层)事务中。(如果被调用方法发生异常,那么调用方法和被调用方法的事务都将回滚)这是最常用的选择。
  • SUPPORTS
    支持当前(外层)事务,如果当前没有(外层)事务,就以非事务方式执行。
  • MANDATORY
    强制使用当前(外层)事务,如果当前没有(外层)事务,就抛出异常。
  • REQUIRES_NEW
    新建一个事务,并在该事务中运行,如果当前存在(外层)事务,则先将当前(外层)事务挂起
  • NOT_SUPPORTED
    以非事务方式执行操作:如果当前存在(外层)事务,就把当前(外层)事务挂起。
  • NEVER
    以非事务方式执行:如果当前存在(外层)事务,则抛出异常。
  • NESTED
    以嵌套方式执行:如果当前存在(外层)事务,则以嵌套方式独立运行于自己的事务中,不影响当前(外层)事务,而当前(外层)事务如果回滚,则该事务也必须回滚;如果当前不存在(外层)事务,则执行与PROPAGATION.REQUIRED类似的操作。

isolation 隔离级别

  • DEFAULT 使用数据库默认的事务隔离级别
  • READ_UNCOMMITTED 允许读取尚未提交的修改,可能导致脏读、幻读和不可重复读
  • READ_COMMITTED 允许从已经提交的事务读取,可防止脏读,但幻读、不可重复读仍然有可能发生
  • REPEATABLE_READ 对相同字段的多次读取的结果是一致的,除非数据被当前事务自身修改。可防止脏读和不可重复读,但幻读仍有可能发生
  • SERIALIZABLE 完全服从ACID隔离原则,确保不发生脏读、不可重复读、和幻读,但执行效率最低。

readOnly 是否只读事务
表示该事务是否只读取数据但不更新数据,只有读取操作的事务设置为只读可以帮助数据库引擎优化事务。

timeout 超时时间
避免对数据库长时间锁定,单位秒。

rollbackFor 回滚异常类
默认只对运行时异常进行回滚,参见下文。

声明式事务的缺陷

类内部调用

Spring的声明式事务是通过AOP的方式动态代理的,因此会产生一个实现了事务代理类,区分于原始类。考虑一种情况:

  • Service类中的方法A没有声明事务
  • Service中的方法B声明了事务
  • 方法A内部调用方法B

此时,调用Service的方法B,使用的是代理类Service'的方法B,是实现了事务方法的,出现异常会回滚。
但如果调用的是方法A,在代理类的内部使用的是原始类Service的方法B,没有实现事务,会导致无法异常回滚。
解决方法:将B方法拆分到其他类中并使用事务包裹,保证调用的是代理类而非原始类的方法。

错误的使用方式

声明式事务包装的方法在方法抛出异常时会回滚,但是默认只在抛出RuntimeException时回滚。
如果异常被catch住吞掉了,或是抛出了非RuntimeException,那么默认是不回滚的。
解决方法:不要吞异常,遇到非RuntimeException,使用@Transaction注解的rollbackFor参数进行控制。
Java8中java.lang的部分类分层结构如下(完整结构请查看文章底部参考资料):

java.lang.Object
    ......
    java.lang.Throwable (implements java.io.Serializable)
        java.lang.Error
            java.lang.AssertionError
            java.lang.LinkageError
                java.lang.BootstrapMethodError
                java.lang.ClassCircularityError
                java.lang.ClassFormatError
                    java.lang.UnsupportedClassVersionError
                java.lang.ExceptionInInitializerError
                java.lang.IncompatibleClassChangeError
                    java.lang.AbstractMethodError
                    java.lang.IllegalAccessError
                    java.lang.InstantiationError
                    java.lang.NoSuchFieldError
                    java.lang.NoSuchMethodError
                java.lang.NoClassDefFoundError
                java.lang.UnsatisfiedLinkError
                java.lang.VerifyError
            java.lang.ThreadDeath
            java.lang.VirtualMachineError
                java.lang.InternalError
                java.lang.OutOfMemoryError
                java.lang.StackOverflowError
                java.lang.UnknownError
        java.lang.Exception
            java.lang.CloneNotSupportedException
            java.lang.InterruptedException
            java.lang.ReflectiveOperationException
                java.lang.ClassNotFoundException
                java.lang.IllegalAccessException
                java.lang.InstantiationException
                java.lang.NoSuchFieldException
                java.lang.NoSuchMethodException
            // 这里是RuntimeException
            java.lang.RuntimeException
                java.lang.ArithmeticException
                java.lang.ArrayStoreException
                java.lang.ClassCastException
                java.lang.EnumConstantNotPresentException
                java.lang.IllegalArgumentException
                    java.lang.IllegalThreadStateException
                    java.lang.NumberFormatException
                java.lang.IllegalMonitorStateException
                java.lang.IllegalStateException
                java.lang.IndexOutOfBoundsException
                    java.lang.ArrayIndexOutOfBoundsException
                    java.lang.StringIndexOutOfBoundsException
                java.lang.NegativeArraySizeException
                java.lang.NullPointerException
                java.lang.SecurityException
                java.lang.TypeNotPresentException
                java.lang.UnsupportedOperationException
    ......

参考资料

Spring声明式事务 - 简书
spring声明式事务 同一类内方法调用事务失效-云栖社区-阿里云 (ps. 这不是原文,我实在找不着原文了)
java.lang Class Hierarchy (Java Platform SE 8 )

本文搬自我的博客,欢迎参观!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,658评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,482评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,213评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,395评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,487评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,523评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,525评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,300评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,753评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,048评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,223评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,905评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,541评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,168评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,417评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,094评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,088评论 2 352

推荐阅读更多精彩内容