事务管理

提示
@Transactional注解只能应用到public可见度的方法上,可以被应用于接口定义和接口方法,方法会覆盖类上面声明的事务。

示例:

例如用户新增需要插入用户表、用户与岗位关联表、用户与角色关联表,如果插入成功,那么一起成功,如果中间有一条出现异常,那么回滚之前的所有操作, 这样可以防止出现脏数据,就可以使用事务让它实现回退。
做法非常简单,我们只需要在方法或类添加@Transactional注解即可。

@Transactional
public int insertUser(User user)
{// 新增用户信息int rows = userMapper.insertUser(user);// 新增用户岗位关联insertUserPost(user);// 新增用户与角色管理insertUserRole(user);return rows;
}

常见坑点

常见坑点1:遇到检查异常时,事务开启,也无法回滚。 例如下面这段代码,用户依旧增加成功,并没有因为后面遇到检查异常而回滚!!

@Transactional
public int insertUser(User user) throws Exception
{// 新增用户信息int rows = userMapper.insertUser(user);// 新增用户岗位关联insertUserPost(user);// 新增用户与角色管理insertUserRole(user);// 模拟抛出SQLException异常boolean flag = true;if (flag){throw new SQLException("发生异常了..");}return rows;
}

原因分析:因为Spring的默认的事务规则是遇到运行异常(RuntimeException)和程序错误(Error)才会回滚。如果想针对检查异常进行事务回滚,可以在@Transactional注解里使用 rollbackFor属性明确指定异常。

例如下面这样,就可以正常回滚:

@Transactional(rollbackFor = Exception.class)
public int insertUser(User user) throws Exception
{// 新增用户信息int rows = userMapper.insertUser(user);// 新增用户岗位关联insertUserPost(user);// 新增用户与角色管理insertUserRole(user);// 模拟抛出SQLException异常boolean flag = true;if (flag){throw new SQLException("发生异常了..");}return rows;
}

常见坑点2:在业务层捕捉异常后,发现事务不生效。 这是许多新手都会犯的一个错误,在业务层手工捕捉并处理了异常,你都把异常“吃”掉了,Spring自然不知道这里有错,更不会主动去回滚数据。

例如:下面这段代码直接导致用户新增的事务回滚没有生效。

@Transactional
public int insertUser(User user) throws Exception
{// 新增用户信息int rows = userMapper.insertUser(user);// 新增用户岗位关联insertUserPost(user);// 新增用户与角色管理insertUserRole(user);// 模拟抛出SQLException异常boolean flag = true;if (flag){try{// 谨慎:尽量不要在业务层捕捉异常并处理throw new SQLException("发生异常了..");}catch (Exception e){e.printStackTrace();}}return rows;
}

推荐做法:在业务层统一抛出异常,然后在控制层统一处理。

@Transactional
public int insertUser(User user) throws Exception
{// 新增用户信息int rows = userMapper.insertUser(user);// 新增用户岗位关联insertUserPost(user);// 新增用户与角色管理insertUserRole(user);// 模拟抛出SQLException异常boolean flag = true;if (flag){throw new RuntimeException("发生异常了..");}return rows;
}

一、注意事项

  • 不要在接口上声明@Transactional ,而要在具体类的方法上使用 @Transactional 注解,不然注解可能无效。
  • 不要将@Transactional放置在类级的声明中,放在类声明,会使得全部方法都有事务。所以@Transactional应该放在方法级别,不需要使用事务的方法,就不要放置事务,好比查询方法。不然对性能是有影响的。
  • 使用了@Transactional的方法,对同一个类里面的方法调用, @Transactional无效。
    好比有一个类Test,它的一个方法A,A再调用Test本类的方法B(无论B是否public仍是private),但A没有声明注解事务,而B有。则外部调用A以后,B的事务是不会起做用的。(常常在这里出错)
  public class Test{public void A(){ B(); }@Transactionalpublic void B(){  }
}

二、异常回滚效果

  1. 使用了@Transactional的方法,只能是public,@Transactional注解的方法都是被外部其余类调用才有效,故只能是public。道理和上面的有关联。故在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不会报错,但事务无效。

  2. 抛出受检查异常XXXException,事务会回滚。

  3. 抛出运行时异常NullPointerException,事务会回滚。

  4. 在service中加上@Transactional,如果是直接调该方法,会回滚,如果是间接调用,不会回滚。(即上文3提到的)

  5. 在service中的private加上@Transactional,事务不会回滚。

注意点

  1. Spring默认情况下会对(RuntimeException)及其子类来进行回滚,在遇见Exception及其子类的时候则不会进行回滚操作。
  2. @Transactional既可以作用于接口,接口方法上以及类上,类的方法上。但是Spring官方不建议接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。另外,@Transactional注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。如果你在protected、private或者默认可见性的方法上使用@Transactional 注解,这将被忽略,也不会抛出任何异常。
  3. Spring默认使用的是jdk自带的基于接口的代理,而没有使用基于类的CGLIB代理。
  4. 如果在接口、实现类或方法上都指定了@Transactional 注解,则优先级顺序为:方法>实现类>接口;
  5. 建议只在实现类或实现类的方法上使用@Transactional,而不要在接口上使用,这是因为如果使用JDK代理机制(基于接口的代理)是没问题;而使用CGLIB代理(继承)机制时就会遇到问题,因为其使用基于类的代理而不是接口,这是因为接口上的@Transactional注解是“不能继承的”;

测试

    @Overridepublic  void test(User user){test1(user);}@Transactionalpublic  void test1(User user){test2(user);test3(user);test4(user);}public  void test2(User user){int i = userMapper.addUser(user);}public void test3(User user){user.setUsername("333");int i = userMapper.updateUser(user);throw new RuntimeException("制造一个异常");//制造一个异常//抛异常按理应该会滚,addUser(user)执行了,test3的updateUser(user)没执行,//但运行后test2,test3两个方法都执行了,什么鬼?//要用代理对象调用,事务才会生效}public void test4(User user){user.setUsername("444");int i = userMapper.updateUser(user);}

结果

以上代码运行结果:方法test3()抛出异常,事务不会进行回滚,方法test2和test3都执行了,test4不执行,即使在test2,test3,test4都加事务注解@Transactional,也不会回滚

分析

  1. 因为spring是基于接口代理,即JDk动态代理去处理事务的,这就说明是基于方法拦截的(记住这点)
  2. 那么spring进入第一个方法test()时,它没有开启事务,就对里面嵌套的其他方法也忽略了,这也是正常的处理逻辑。
  3. 试想一下,如果spring不是这样处理就会出现逻辑混乱。比如此时test()方法里面执行了一些DB操作,然后再调用其他方法test1(),如果发生异常,此时test()里面的DB操作部分没有回滚,test1()里面却回滚了?那岂不是出现了逻辑混乱?

解决

    @Transactional@Overridepublic  void test(User user){test1(user);}public  void test1(User user){test2(user);test3(user);test4(user);}public  void test2(User user){int i = userMapper.addUser(user);}public void test3(User user){user.setUsername("333");int i = userMapper.updateUser(user);throw new RuntimeException("制造一个异常");//制造一个异常}public void test4(User user){user.setUsername("444");int i = userMapper.updateUser(user);}

结果

以上代码运行结果,方法test3()抛出异常,事务进行回滚,方法test2,test3,test4都不执行。

try catch

    @Transactional@Overridepublic  void test(User user){int i = userMapper.addUser(user);//把test2(user)放在try里面目的:抓住异常,不管test2(user)有没有异常,都可以正常执行addUser(user)方法try {UserService proxy=(UserService) AopContext.currentProxy();//获取代理对象proxy.test2(user);//用代理对象调用方法开启事务//test2(user);//单独调用时} catch (Exception e) {logger.error("修改出现了异常",e.getMessage());}}@Transactional@Overridepublic void test2(User user){user.setUsername("888");int i = userMapper.updateUser(user);int x=1/0;//制造一个异常// throw new RuntimeException("制造一个异常");//制造一个异常}

总结

1.在方法里没有try catch抓异常时,被调用的方法入口处如果没有加事务注解,那么方法内调用其他的方法(不管其他方法前面有没有加事务注解),其他的方法的事务都不会生效;被调用的方法入口处如果加了事务注解,那么方法内调用其他的方法(不管其他方法前面有没有加事务注解),其他的方法的事务都会生效。

2.在方法里有try catch抓异常时,(如下代码)都加了事务注解,当抛出异常后,两个方法都不会回滚,并且数据库记录都发生改变(事务失效)事务失效解决:
获取当前对象的动态代理对象即可。

三、@Transactional()添加属性

Exception异常

  1. 让Exception异常也进行回滚操作,在调用该方法前加上:
 @Transactional(rollbackFor = Exception.class)
  1. 让RuntimeException不进行回滚操作,在调用该方法前加上:
 @Transactional(noRollbackFor=RunTimeException.class)

只读事务

  1. 在整个方法运行前就不会开启事务:
    这样就做成一个只读事务,可以提高效率
@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)

更多

查看方法的事务是否已经被执行

TransactionSynchronizationManager.isActualTransactionActive()

返回true:该方法中存在事务,false则不存在

事务的传播及其属性的意义:

//如果有事务,那么加入事务,没有的话新创建一个
@Transactional(propagation=Propagation.REQUIRED)//这个方法不开启事务
@Transactional(propagation=Propagation.NOT_SUPPORTED)//不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务
@Transactional(propagation=Propagation.REQUIREDS_NEW)//必须在一个已有的事务中执行,否则抛出异常
@Transactional(propagation=Propagation.MANDATORY)//不能在一个事务中执行,就是当前必须没有事务,否则抛出异常
@Transactional(propagation=Propagation.NEVER)//其他bean调用这个方法,如果在其他bean中声明了事务,就是用事务。没有声明,就不用事务。
@Transactional(propagation=Propagation.SUPPORTS)//如果一个活动的事务存在,则运行在一个嵌套的事务中,如果没有活动的事务,则按照REQUIRED属性执行,它使用一个单独的事务。这个书屋拥有多个回滚的保存点,内部事务的回滚不会对外部事务造成影响,它只对DataSource TransactionManager事务管理器起效。
@Transactional(propagation=Propagation.NESTED)//只读,不能更新,删除
@Transactional(propagation=Propagation.REQUIRED,readOnly=true)//超时30秒
@Transactional(propagation=Propagation.REQUIRED,timeout=30)//数据库隔离级别
@Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.DEFAULT)

@Transactional事务的使用和注意事项及其属性相关推荐

  1. @Transactional事务几点注意

    这里面有几点需要大家留意: A. 一个功能是否要事务,必须纳入设计.编码考虑.不能仅仅完成了基本功能就ok. B. 如果加了事务,必须做好开发环境测试(测试环境也尽量触发异常.测试回滚),确保事务生效 ...

  2. Spring中@Transactional事务回滚(含实例详细讲解,附源码)

    一.使用场景举例 在了解@Transactional怎么用之前我们必须要先知道@Transactional有什么用.下面举个栗子:比如一个部门里面有很多成员,这两者分别保存在部门表和成员表里面,在删除 ...

  3. @Transactional事务生效条件与样例

    @Transactional事务生效条件 @Transactional注释的方法,不能是private修饰 @Transactional注释的方法,必须是有接口的方法实现(通用的Spring面向接口编 ...

  4. SpringBoot 异常回滚 事务的使用___Springboot @Transactional 事务不回滚

    Springboot中事务的使用: 1.启动类加上@EnableTransactionManagement注解,开启事务支持(其实默认是开启的). 2.在使用事务的public(只有public支持事 ...

  5. 【Spring源码】Spring Transactional事务:传播机制(Propagation) 介绍 和 源码剖析

    [Spring源码]Spring Transactional事务:传播机制(Propagation) 源码剖析 关键词 AMethod调用BMethod,转载BMethod的角度来考虑:站在被调用者的 ...

  6. @Transactional事务中使用锁坑(@Transactional事务中使用锁失效)

    @Transactional事务中使用锁失效 说明: Spring中使用注解@Transactional作事务管理,@Transactional注解在方法上时,是方法完成之后才进行提交事务的 测试代码 ...

  7. @Transactional的用法详解及Transactional事务无效的源码分析

    数据库事务正确执行的四要素 1.原子性 事务是不可分割的最小的工作单元,事务内的操作要么全做,要么全不做,不能只做一部分. 2.一致性 事务执行前数据库的数据按照逻辑处于正确的状态,事务执行后数据库的 ...

  8. Java的@Transactional事务回滚

    @Transactional 基本原理概述 在应用系统调用声明@Transactional 的目标方法时,Spring Framework 默认使用 AOP 代理,在代码运行时生成一个代理对象,根据@ ...

  9. Spring的@Transactional事务注意事项

    1.@Transactional应该放在方法级别,不需要使用事务的方法,就不要放置事务, 2.查询方法声明不要事务,否则对性能是有影响的. 3.对同一个类里的方法调用, @Transactional无 ...

最新文章

  1. 李宏毅机器学习笔记(五)-----Where does the error come from
  2. 学java教程之this关键字
  3. SAP 电商云 Spartacus UI 里的 InjectionToken 应用场景
  4. [TJOI2011] 书架(线段数优化dp + 单调栈)
  5. Oracle入门(三)之连接与登录
  6. python实现k均值算法_python实现kMeans算法
  7. SEO的十种赚钱方式
  8. 第八章Transact-SQL程序设计
  9. 代码规范 设计模式落地之路
  10. 化繁就简 · 万物互联,华为云All-Connect企业级云网络正式发布
  11. 常用Latex表达式符号——组合数学篇
  12. eclipse中的JSP项目连接mysql报错,找不到jdbc驱动,java项目却没问题
  13. 网络编程基础【林老师版】:简单的 套接字通信(一)
  14. 《Spring 5 官方文档》5. 验证、数据绑定和类型转换(二)
  15. exit()和_exit()的区别
  16. Failure to find com.rongpd:rpd:pom:1.0 in xxx was cached in the local repository, resolution will no
  17. Freebsd-9.0 how to change default sound device
  18. word英文字体下载 如Caecilia LT Std
  19. 大话数据结构(个人笔记)
  20. 8583:全面掌握ISO8583报文协议zz

热门文章

  1. 将单链表的每K个节点之间逆序
  2. 机器学习笔记: Upsampling, U-Net, Pyramid Scene Parsing Net
  3. Python 知识点全解析系列之列表推导式(list comprehension)
  4. 【云计算】3_云网络产品介绍
  5. 力扣(leetcode)-1. 两数之和
  6. pandas中的括号索引
  7. 【LeetCode从零单排】No22.Generate Parentheses
  8. 【LeetCode从零单排】No112 Path Sum
  9. redis中的事务、lua脚本和管道的使用场景
  10. 深入分析 Java I/O 的工作机制--转载