Java Spring事务管理与@Transactional注解式事务详解

  • spring事务管理是指在业务代码在出现异常之后,对之前的操作进行回滚,保证数据库数据的一致性

  • 分为编程式事务管理,配置式,注解式

    • 编程式
    TransactionDefinition def = new DefaultTransactionDefinition();
    TransactionStatus status = transactionManager.getTransaction(def)
    try{
      businessLogic();
      transactionManager.commit(status);
    }
    catch(Exception ex) {
      transactionManager. rollback(status);
      throw ex;
    }
    
    • xml配置式
    <tx:advice id="txAdvice" transaction-manager="txManager">
    <tx:attributes>
      <tx:method name="create*" propagation="REQUIRED" timeout="300" rollback-for="java.lang.Exception" />
      <tx:method name="delete*" propagation="REQUIRED" timeout="300" rollback-for="java.lang.Exception" />
      <tx:method name="update*" propagation="REQUIRED" timeout="300" rollback-for="java.lang.Exception" />
      <tx:method name="get*" propagation="REQUIRED" read-only="true" timeout="300" />
      <tx:method name="*" propagation="REQUIRED" read-only="true" timeout="300" />
    </tx:attributes>
    </tx:advice>
    
    <aop:config>
    <aop:pointcut id="txPointcut" expression="execution(* com.mico.emptyspring.service.*ServiceA.*(..))" />
    <aop:advisor pointcut-ref="txPointcut" advice-ref="txAdvice" />
    </aop:config>
    
    • 注解式,配置文件上添加配置<tx:annotation-driven transaction-manager="txManager"/>,接着在service类或方法上添加@Transactional注解
  • 先偷个鸡,事务注解可以放在控制器层吗?

    • 可以,但是在spring-framework-reference.pdf文档中有这样一段话:
      <tx:annotation-driven/> only looks for @Transactional on beans in the same application context it is defined in. This means that, if you put <tx:annotation-driven/> in a WebApplicationContext for a DispatcherServlet, it only checks for @Transactional beans in your controllers, and not your services.这句话的意思是,<tx:annoation-driven/>只会查找和它在相同的应用上下文文件[同一配置文件]中定义的bean上面的@Transactional注解,如果你把它放在Dispatcher的应用上下文中,它只检查控制器上的@Transactional注解,而不是你services上的@Transactional注解。所以org.springframework.web.servlet.DispatcherServletcontextConfigLocation属性配置的xml中配置<tx:annotation-driven/>则spring只检查控制器上的@Transactional注解,而不是你services上的@Transactional注解。但是,不推荐这么做,嘻嘻
  • @Transactional配置项

属性 类型 描述
value String 可选的限定描述符,指定使用的事务管理器
propagation enum: Propagation 可选的事务传播行为设置
isolation enum: Isolation 可选的事务隔离级别设置
readOnly boolean 读写或只读事务,默认读写
timeout int (in seconds granularity) 事务超时时间设置
rollbackFor Class对象数组,必须继承自Throwable 导致事务回滚的异常类数组
rollbackForClassName 类名数组,必须继承自Throwable 导致事务回滚的异常类名字数组
noRollbackFor Class对象数组,必须继承自Throwable 不会导致事务回滚的异常类数组
noRollbackForClassName 类名数组,必须继承自Throwable 不会导致事务回滚的异常类名字数组
  • value主要用来指定不同的事务管理器;主要用来满足在同一个系统中,存在不同的事务管理器。比如在Spring中,声明了两种事务管理器txManager1, txManager2.然后,用户可以根据这个参数来根据需要指定特定的txManager.在一个系统中,需要访问多个数据源或者多个数据库,则必然会配置多个事务管理器的

  • 脏读,不可重复读和幻象读

    • Dirty reads--读脏数据。也就是说,比如事务A的未提交(还依然缓存)的数据被事务B读走,如果事务A失败回滚,会导致事务B所读取的的数据是错误的。
    • non-repeatable reads--数据不可重复读。比如事务A中两处读取数据-total-的值。在第一读的时候,total是100,然后事务B就把total的数据改成 200,事务A再读一次,结果就发现,total竟然就变成200了,造成事务A数据混乱。
    • phantom reads--幻象读数据,这个和non-repeatable reads相似,也是同一个事务中多次读不一致的问题。但是non-repeatable reads的不一致是因为他所要取的数据集被改变了(比如total的数据),但是phantom reads所要读的数据的不一致却不是他所要读的数据集改变,而是他的条件数据集改变。比如Select account.id where account.name="ppgogo*",第一次读去了6个符合条件的id,第二次读取的时候,由于事务b把一个帐号的名字由"dd"改成"ppgogo1",结果取出来了7个数据。
  • 事务的隔离级别:是指若干个并发的事务之间的隔离程度

    • @Transactional(isolation = Isolation.READ_UNCOMMITTED):读取未提交数据(会出现脏读, 不可重复读) 基本不使用。
    • @Transactional(isolation = Isolation.READ_COMMITTED):读取已提交数据(会出现不可重复读和幻读)大多数主流数据库的默认事务等级,保证了一个事务不会读到另一个并行事务已修改但未提交的数据,避免了“脏读取”。该级别适用于大多数系统。
    • @Transactional(isolation = Isolation.REPEATABLE_READ):可重复读(会出现幻读),保证了一个事务不会修改已经由另一个事务读取但未提交(回滚)的数据。避免了“脏读取”和“不可重复读取”的情况,但是带来了更多的性能损失。
    • @Transactional(isolation = Isolation.SERIALIZABLE):最严格的级别,事务串行执行,资源消耗最大;
  • 事务隔离级别和脏读,幻象读,不可重复读

Isolation Dirty reads Non-repeatable reads Phantom reads
Serializable 不会 不会 不会
REPEATABLE READ 不会 不会
READ COMMITTED 不会
Read Uncommitted
  • 事务传播属性:如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为,即事务方法的嵌套调用会产生事务传播

    • TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值。
    • TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。
    • TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
    • TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
    • TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
    • TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
    • TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。
    • REQUIRED_NEW和NESTED两种不同的传播机制的区别:
      • REQUIRED_NEW:内部的事务独立运行,在各自的作用域中,可以独立的回滚或者提交;而外部的事务将不受内部事务的回滚状态影响.启动一个新的, 不依赖于环境的 “内部” 事务. 这个事务将被完全 commited 或 rolled back 而不依赖于外部事务, 它拥有自己的隔离范围, 自己的锁, 等等. 当内部事务开始执行时, 外部事务将被挂起, 内务事务结束时, 外部事务将继续执行.
      • NESTED:的事务,基于单一的事务来管理,提供了多个保存点。这种多个保存点的机制允许内部事务的变更触发外部事务的回滚。如果外部事务 commit, 嵌套事务也会被 commit;如果外部事务 roll back, 嵌套事务也会被 roll back 。开始一个 “嵌套的” 事务, 它是已经存在事务的一个真正的子事务. 嵌套事务开始执行时, 它将取得一个 savepoint. 如果这个嵌套事务失败, 我们将回滚到此 savepoint. 嵌套事务是外部事务的一部分, 只有外部事务结束后它才会被提交
  • @Transactional 注意事项

    • 用在接口实现类或接口实现方法上,而不是接口类中,Spring 建议不要在接口或者接口方法上使用@Transactional注解,因为这只有在使用基于接口的代理时它才会生效
    • @Transactional 注解应该只被应用到 public 方法上,
      这是由 Spring AOP 的本质决定的。如果你在 protected、private 或者默认可见性的方法上使用 @Transactional 注解,这将被忽略,也不会抛出任何异常。
    • 将@Transactional放置在需要进行事务管理的方法上,而不是不假思索的放置在接口实现类上( 接口中所有方法都需要进行事务管理,但其实并不需要,如只读的接口就不需要事务管理,但是 由于配置了@Transactional就需要AOP拦截及事务的处理,影响系统性能)
    • 方法上注解属性会覆盖类注解上的相同属性,当接口与接口中方法上同时带有@Transactional注解时,方法上的事务注解会覆盖类上的注解
    • 用 spring 事务管理器,由spring来负责数据库的打开,提交,回滚.默认遇到运行期例外(throw new RuntimeException(“注释”);)会回滚,即遇到不受检查(unchecked)的例外时回滚;而遇到需要捕获的例外(throw new Exception(“注释”);)不会回滚,即遇到受检查的例外(就是非运行时抛出的异常,编译器会检查到的异常叫受检查例外或说受检查异常)时,需我们指定方式来让事务回滚要想所有异常都回滚,要加上 @Transactional( rollbackFor={Exception.class,其它异常}) .如果让unchecked例外不回滚:@Transactional(notRollbackFor=RunTimeException.class)
    • 默认情况下,只有来自外部的方法调用才会被AOP代理捕获,也就是,类内部方法调用本类内部的其他方法并不会引起事务行为,即使被调用方法使用@Transactional注解进行修饰,如下
      • 前置条件:同一个类中方法,A方法无@Transactional 注解,B使用了
        • 同一个类中A 调用 B ;则外部调用A之后,B的事务是不会起作用的
        • 若是有上层(按照 Controller层、Service层、DAO层的顺序)由Controller 调用 Service 直接调用B方法,发生异常会发生回滚;若间接调用,Action 调用 Service 中 的A 方法,A无@Transactional 注解,B有,A调用B,B的注解无效
    • 父类的声明的@Transactional会对子类的所有方法进行事务增强;子类覆盖重写父类方式可覆盖其@Transactional中的声明配置
  • 多线程下事务管理

    • 因为线程不属于spring托管,故线程不能够默认使用spring的事务,也不能获取spring注入的bean
    • 在被spring声明式事务管理的方法内开启多线程,多线程内的方法不被事务控制
    • 单线程的情况下,一个事务会在层级式调用的Spring组件之间传播。但是在@Transactional注解的服务方法会产生一个新的线程的情况下,事务是不会从调用者线程传播到新建线程的。结果会是被调用者线程会说缺少事务,
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 156,907评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,546评论 1 289
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,705评论 0 238
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,624评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,940评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,371评论 1 210
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,672评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,396评论 0 195
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,069评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,350评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,876评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,243评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,847评论 3 231
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,004评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,755评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,378评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,266评论 2 259