事务的目的就是要保证数据的高度完整性和一致性。

在实际的项目中,大多都是使用注解的方式来实现事物,这里也就简单记录下使用@Transactional方法和注意事项.

  1. 在xml中添加配置

    1
    2
    3
    4
    5
    6
    7
    <!-- TransactionManager -->
    <bean id="transactionManager"
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
    </bean>
    <!-- 如果没有它,@Transactional不会生效 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>
  2. 使用
    在接口或类的声明处 ,写一个@Transactional 如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
      // 业务方法需要在一个事物中运行,如果方法运行时,已经存在一个事物中,  
    // 那么加入该事物,否则为自己创建一个新事物。(默认)
    @Transactional(propagation = Propagation.REQUIRED)
    public void test1() {
    } // 声明方法不需要事务,如果方法没有关联到一个事务,容器不会为它开启事物。
    // 如果方法在一个事物中被调用,该事物会被挂起,在方法调用结束后,原先的
    // 事物便会恢复执行。
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void test2() {
    }
    // 表明不管是否存在事物,业务方法总会为自己发起一个新事物。
    // 如果方法已经运行在一个事物中,则原有事物会被挂起,
    // 新的事物会被创建,直到方法执行结束,新事物才算结束,
    // 原先的事务才会被恢复执行。
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void test3() {
    }
    // 该属性指定业务方法只能在一个已经存在的事物中执行,
    // 业务方法不能发起自己的事物,如果业务方法在没有事物的环境
    // 下调用,容器就会抛出异常。
    @Transactional(propagation = Propagation.MANDATORY)
    public void test4() {
    }
    // 这个事物属性表明,如果业务方法在某个事务范围内被调用,则方法成为该事物的一部分,
    // 如果业务方法在事务范围外被调用,则方法在没有事物的环境下执行。
    @Transactional(propagation = Propagation.SUPPORTS)
    public void test5() {
    }
    // 指定业务方法绝对不能在事物范围内执行。如果业务方法在某个事物中执行,
    // 容器会抛出异常,只有业务方法没有关联到任何事物,才能正常执行。
    @Transactional(propagation = Propagation.NEVER)
    public void test6() {
    }
    // 如果一个活动的事物存在,则运行在一个嵌套的事物中,如果没有活动事物,
    // 则按REQUIRED属性执行,它使用了一个单独的事物,这个事物拥有多个回滚的保存点,
    // 内部事务的回滚不会对外事物造成影响,它只对DataSourceTransactionManager
    // 事务管理器起效。
    @Transactional(propagation = Propagation.NESTED)
    public void test7() {
    }
    @Transactional(isolation = Isolation.DEFAULT)
    public void test8() {
    }
    // 读已提交数据(会出现不可重复读和幻读)
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public void test9() {
    }
    // 读未提交数据(会出现脏读、不可重复读和幻读)
    @Transactional(isolation = Isolation.READ_UNCOMMITTED)
    public void test10() {
    }
    // 可重复读(会出现幻读)
    @Transactional(isolation = Isolation.REPEATABLE_READ)
    public void test11() {
    }
    // 串行化
    @Transactional(isolation = Isolation.SERIALIZABLE)
    public void test12() {
    }
    // 抛出Exception异常时,记录回滚
    @Transactional(rollbackFor = Exception.class)
    public void test13() throws Exception {
    }
  3. 参数说明
    readOnly: 该属性用于设置当前事务是否为只读事务,设置为true表示只读,false则表示可读写,默认值为false。例如:@Transactional(readOnly=true),要想只读生效,必须启动一个事务。而恰恰我们只是读取时,根本不需要事物,所以这个标识有点无意义.

    rollbackFor: 该属性用于设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚。例如:
    指定单一异常类:@Transactional(rollbackFor=RuntimeException.class)
    指定多个异常类:@Transactional(rollbackFor={RuntimeException.class, Exception.class})

    rollbackForClassName: 该属性用于设置需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,则进行事务回滚。例如:
    指定单一异常类名称:@Transactional(rollbackForClassName=”RuntimeException”)
    指定多个异常类名称:@Transactional(rollbackForClassName={“RuntimeException”,”Exception”})

    noRollbackFor: 该属性用于设置不需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,不进行事务回滚。例如:
    指定单一异常类:@Transactional(noRollbackFor=RuntimeException.class)
    指定多个异常类:@Transactional(noRollbackFor={RuntimeException.class, Exception.class})

    noRollbackForClassName: 该属性用于设置不需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,不进行事务回滚。例如:
    指定单一异常类名称:@Transactional(noRollbackForClassName=”RuntimeException”)
    指定多个异常类名称:
    @Transactional(noRollbackForClassName={“RuntimeException”,”Exception”}) propagation:该属性用于设置事务的传播行为,例如:@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)

    isolation: 该属性用于设置底层数据库的事务隔离级别,事务隔离级别用于处理多事务并发的情况,通常使用数据库的默认隔离级别即可,基本不需要进行设置

    timeout: 该属性用于设置事务的超时秒数,默认值为-1表示永不超时

  4. 注意

  • @Transactional 只能被应用到public方法上, 对于其它非public的方法,如果标记了@Transactional也不会报错,但方法没有事务功能.
  • 用 Spring 事务管理器,由spring来负责数据库的打开,提交,回滚.默认遇到运行期例外(throw new RuntimeException(“注释”);)会回滚,即遇到不受检查(unchecked)的例外时回滚;而遇到需要捕获的例外(throw new Exception(“注释”);)不会回滚,需我们以.指定方式来让事务回滚。
  • @Transactional 注解可以被应用于接口定义和接口方法、类定义和类的 public 方法上。然而,请注意仅使用 @Transactional 注解不足于开启事务行为,它仅仅 是一种元数据,能够被可以识别 @Transactional 注解和上述的配置适当的具有事务行为的beans所使用。上面的例子中,其实正是 元素的出现 开启 了事务行为。
  • Spring团队的建议是你在具体的类(或类的方法)上使用 @Transactional 注解,而不要使用在类所要实现的任何接口上。你当然可以在接口上使用 @Transactional 注解,但是这将只能当你设置了基于接口的代理时它才生效。因为注解是 不能继承 的,这就意味着如果你正在使用基于类的代理时,那么事务的设置将不能被基于类的代理所识别,而且对象也将不会被事务代理所包装(将被确认为严重的)。因 此,请接受Spring团队的建议并且在具体的类上使用 @Transactional 注解。
  • 在单独使用不带任何参数的 @Transactional 注释时,传播模式默认设置为 REQUIRED,只读标志设置为 false,事务隔离级别设置为 READ_COMMITTED,而且事务不会针对受控异常(checked exception)回滚。

遇到的坑

在一个方法中,我们需要统计商品每天的销量并保存和更新商品的总销量,如:

1
2
3
4
5
6
7
8
9
10
11
12
@Transactional
public ResultBase executeSalesStat(){
try {
//保存每日商品销量信息
productDaySalesStatDAO.saveProductSalesStat();
//更新商品总销量
productDaySalesStatDAO.updateProductSalesStat();
return new ResultBase();
} catch (Exception e) {
throw new Exception(e);
}
}

假设我们在执行更新updateProductSalesStat();方法时出现了异常DataIntegrityViolationException,结果就是每日商品销量信息保存成功,而更新商品总销量没有成功,说明事物没有达到我们想要的结果,回滚保存每日销量。
解决办法想到了两个:
1.使用rollbackFor 参数,指定要回滚的异常:@Transactional(rollbackFor = Exception.class)
2.抛出运行时异常,即:

1
2
3
4
5
6
7
8
9
try {
//保存每日商品销量信息
productDaySalesStatDAO.saveProductSalesStat();
//更新商品总销量
productDaySalesStatDAO.updateProductSalesStat();
return new ResultBase();
} catch (RuntimeException e) {
throw new RuntimeException(e);
}

参考IBM Spring 事务策略

Spring事务管理笔记的更多相关文章

  1. Spring 事务管理笔记

    本文为 Spring 框架的事务管理学习笔记,官网文档地址为:Transaction Management,隔离级别及传播属性解释来自 org.springframework.transaction. ...

  2. 阿里面试挂了,就因为面试官说我Spring 事务管理(器)不熟练?

    前言 事务管理,一个被说烂的也被看烂的话题,还是八股文中的基础股之一.但除了八股文中需要熟读并背诵的那些个传播行为之外,背后的"为什么"和核心原理更为重要. ​ 写这篇文章之前,我 ...

  3. 【Java EE 学习 52】【Spring学习第四天】【Spring与JDBC】【JdbcTemplate创建的三种方式】【Spring事务管理】【事务中使用dbutils则回滚失败!!!??】

    一.JDBC编程特点 静态代码+动态变量=JDBC编程. 静态代码:比如所有的数据库连接池 都实现了DataSource接口,都实现了Connection接口. 动态变量:用户名.密码.连接的数据库. ...

  4. spring事务管理器设计思想(二)

    上文见<spring事务管理器设计思想(一)> 对于第二个问题,涉及到事务的传播级别,定义如下: PROPAGATION_REQUIRED-- 如果当前没有事务,就新建一个事务.这是最常见 ...

  5. spring事务管理器设计思想(一)

    在最近做的一个项目里面,涉及到多数据源的操作,比较特殊的是,这多个数据库的表结构完全相同,由于我们使用的ibatis框架作为持久化层,为了防止每一个数据源都配置一套规则,所以重新实现了数据源,根据线程 ...

  6. 事务管理(下) 配置spring事务管理的几种方式(声明式事务)

    配置spring事务管理的几种方式(声明式事务) 概要: Spring对编程式事务的支持与EJB有很大的区别.不像EJB和Java事务API(Java Transaction API, JTA)耦合在 ...

  7. Spring事务管理器的应对

    Spring抽象的DAO体系兼容多种数据访问技术,它们各有特色,各有千秋.像Hibernate是非常优秀的ORM实现方案,但对底层SQL的控制不太方便:而iBatis则通过模板化技术让你方便地控制SQ ...

  8. Spring事务管理(转)

    1 初步理解 理解事务之前,先讲一个你日常生活中最常干的事:取钱. 比如你去ATM机取1000块钱,大体有两个步骤:首先输入密码金额,银行卡扣掉1000元钱:然后ATM出1000元钱.这两个步骤必须是 ...

  9. [Spring框架]Spring 事务管理基础入门总结.

    前言:在之前的博客中已经说过了数据库的事务, 不过那里面更多的是说明事务的一些锁机制, 今天来说一下Spring管理事务的一些基础知识. 之前的文章: [数据库事务与锁]详解一: 彻底理解数据库事务一 ...

随机推荐

  1. HTML5API

    H5新API 一.地理位置API 1.navigator.geolocation对象 getCurrentPosition(callback,errCallback,options)获取当前位置 wa ...

  2. 80端口被System进程占用问题

    更新: 有可能占用80端口的服务: 如果安装了IIS,关闭IIS: 如果未开启IIS功能,而安装了诸如Web Matrix的开发程序,则有可能被Web Development Agent Servic ...

  3. leetcode 之Reverse Linked List II(15)

    这题用需要非常细心,用头插法移动需要考虑先移动哪个,只需三个指针即可. ListNode *reverseList(ListNode *head, int m, int n) { ListNode d ...

  4. django开发项目实例3--用session是实现简单的登陆、验证登陆和注销功能

    如果你的网页不是纯阅读型的,那么你很有可能希望在用户打开某些界面的时候需要验证用户是否登陆的信息, 虽然django里面有自带的一些user的类,但我看不懂,并且自己实现也不是很难,下面和大家分享一下 ...

  5. 自定义wordCount程序、

    1.MyWordCount代码: package com.hadoop.mr; import java.io.IOException; import org.apache.hadoop.conf.Co ...

  6. Pygame-依葫芦画瓢之兔獾大战

    Pygame-依葫芦画瓢之兔獾大战 前几天看到国外一个12岁的孩子写的兔獾大战游戏,心生敬佩,想当年我还是12岁的时候还不知电脑为何物,连小霸王都未曾玩过.自己也未曾想去搞游戏开发,纯属自娱自乐.在此 ...

  7. WordPress固定链接设置的几种方法(推荐/%post_id%.html)

    传说中,固定链接有SEO功能,今天试了试,现在给大家分享一下: wordpress固定链接设置技巧: 1.不要让日期出现在固定链接里面 这基于两个方面的考虑.一是如果数字出现在固定链接里面,等于提醒搜 ...

  8. 如何把Android Studio项目转换成Eclipse的项目

    1. 找到这个目录:项目名称\app\src\main,并把它导入到Eclipse里 2. 把项目名称\app\libs文件夹拷贝到新生成的项目下,并删除掉.DS_Store文件 3. 这时再看还有什 ...

  9. FastJson过滤掉不需要的返回字段

    /** * @Author:txp * @Description:过滤掉不需要返回的字段 * @Param: [src, clazz, args]` * @Data: 2017/10/10 9:10 ...

  10. 京东前端:PhantomJS 和NodeJS在网站前端监控平台的最佳实践

    1. 为什么需要一个前端监控系统 通常在一个大型的 Web 项目中有很多监控系统,比如后端的服务 API 监控,接口存活.调用.延迟等监控,这些一般都用来监控后台接口数据层面的信息.而且对于大型网站系 ...