java-@Transactional 事务注解

@Transactional 事务注解

  • 前言
    • 一、什么是事务?
    • 二、事务的特性 (具有ACID的特性)
    • 三、@Transactional属性详解
    • 四、注解失效问题


前言

java-@Transactional 事务注解_第1张图片

一、什么是事务?

事务(Transactional) 就是把多个要做的操作组合成一个整体,利用事务的特性来保证操作的安全性,如果一个事务做到一半出现任何错误,就会进行回滚操作,来恢复成最初的模样。

二、事务的特性 (具有ACID的特性)

  1. A 原子性(atomicity) :
    事务是一个不可分割的工作单位,事务中的操作要么都修改,要么都不修改。
  2. C 一致性(consistency):
    事务在完成时,必须是所有的数据都保持一致状态。
  3. I 隔离性(isolation):
    一个事务的执行不能被其他事务所影响。
  4. D 持久性(Durability):
    持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的。

三、@Transactional属性详解

声明式事务管理建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。

  1. @Transactional 注解只能应用到接口方法、类、还有public方法上。
  2. 默认情况下,Spring会对unchecked异常进行事务回滚;如果是checked异常则不回滚。
    checked异常:Error或者RuntimeException(比如空指针,1/0)的异常;
    unchecked异常:继承自java.lang.Exception得异常统称为Checked Exception,如IOException、TimeoutException等。
  3. 只读事务:
    @Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)
    只读标志只在事务启动时应用,否则即使配置也会被忽略。
    启动事务会增加线程开销,数据库因共享读取而锁定(具体跟数据库类型和事务隔离级别有关)。通常情况下,仅是读取数据时,不必设置只读事务而增加额外的系统开销。
    java-@Transactional 事务注解_第2张图片
    java-@Transactional 事务注解_第3张图片
    java-@Transactional 事务注解_第4张图片

四、注解失效问题

  1. @Transactional 应用在非 public 修饰的方法上
    事务拦截器在目标方法执行前后进行拦截,内部会调用方法来获取Transactional 注解的事务配置信息,调用前会检查目标方法的修饰符是否为 public,不是 public则不会获取@Transactional 的属性配置信息。

  2. @Transactional 注解属性 rollbackFor 设置错误
    rollbackFor 可以指定能够触发事务回滚的异常类型。Spring默认抛出了未检查unchecked异常(继承自 RuntimeException 的异常)或者 Error才回滚事务;其他异常不会触发回滚事务。如果在事务中抛出其他类型的异常,但却期望 Spring 能够回滚事务,就需要指定rollbackFor属性。

  3. 同一个类中方法调用,导致@Transactional失效
    开发中避免不了会对同一个类里面的方法调用,比如有一个类Test,它的一个方法A,A再调用本类的方法B(不论方法B是用public还是private修饰),但方法A没有声明注解事务,而B方法有。则外部调用方法A之后,方法B的事务是不会起作用的。这也是经常犯错误的一个地方。

那为啥会出现这种情况?其实这还是由于使用Spring AOP代理造成的,因为只有当事务方法被当前类以外的代码调用时,才会由Spring生成的代理对象来管理。

  1. 异常被你的 catch“吃了”导致@Transactional失效
    如果你手动的catch捕获这个异常并进行处理,事务管理器会认为当前事务应该正常commit,就会导致注解失效,如果非要捕获且不失效,就必须在代码块内throw new Exception抛出异常。

  2. 数据库引擎不支持事务
    开启事务的前提就是需要数据库的支持,我们一般使用的Mysql引擎时支持事务的,所以一般不会出现这种问题。

  3. 开启多线程任务时,事务管理会受到影响
    因为线程不属于spring托管,故线程不能够默认使用spring的事务,也不能获取spring注入的bean在被spring声明式事务管理的方法内开启多线程,多线程内的方法不被事务控制。
    另外,使用多线程事务的情况下,进行回滚,比较麻烦。thread的run方法,有个特别之处,它不会抛出异常,但异常会导致线程终止运行。
    最麻烦的是,在线程中抛出的异常即使在主线程中使用try…catch也无法解释。这非常糟糕,我们必须要“感知”到异常的发生。比如某个线程在处理重要的事务,当thread异常终止,我必须要收到异常的报告,才能回滚事务。这时可以使用线程的UncaughtExceptionHandler进行异常处理,UncaughtExceptionHandler名字意味着处理未捕获的异常。更明确地说,它处理未捕获的运行异常。

你可能感兴趣的:(#,java,java,spring)