网站开发与维护难吗,广告投放是什么工作,网站平台推广,学术ppt模板免费在Spring中进行事务管理非常简单#xff0c;只需要在方法上加上注解Transactional#xff0c;Spring就可以自动帮我们进行事务的开启、提交、回滚操作。甚至很多人心里已经将Spring事务与Transactional划上了等号#xff0c;只要有数据库相关操作就直接给方法加上Transactio… 在Spring中进行事务管理非常简单只需要在方法上加上注解TransactionalSpring就可以自动帮我们进行事务的开启、提交、回滚操作。甚至很多人心里已经将Spring事务与Transactional划上了等号只要有数据库相关操作就直接给方法加上Transactional注解。不瞒你说我之前也一直是这样直到使用Transactional导致了一次生产事故而那次生产事故还导致我当月绩效被打了D...Transactional导致的生产事故19年在公司做了一个内部报销的项目有这样一个业务逻辑1、员工加班打车可以通过滴滴出行企业版直接打车第二天打车费用可以直接同步到我们的报销平台2、员工可以在报销平台勾选自己打车费用并创建一张报销单进行报销创建报销单的同时会创建一条审批流统一流程平台让领导审批当时创建报销单的代码是这么写的/*** 保存报销单并创建工作流*/
Transactional(rollbackFor Exception.class)
public void save(RequestBillDTO requestBillDTO){//调用流程HTTP接口创建工作流workflowUtil.createFlow(BILL,requestBillDTO);//转换DTO对象RequestBill requestBill JkMappingUtils.convert(requestBillDTO, RequestBill.class);requestBillDao.save(requestBill);//保存明细表requestDetailDao.save(requestBill.getDetail())
}代码非常简单也很 “优雅”先通过http接口调用工作流引擎创建审批流然后保存报销单而为了保证操作的事务在整个方法上加上了Transactional注解仔细想想这样真的能保证事务吗。报销项目属于公司内部项目本身是没什么高并发的系统也一直稳定运行着。在年末的一天下午前几天刚好下了大雪打车的人特别多公司发通知邮件说年度报销窗口即将关闭需要尽快将未报销的费用报销掉而刚好那天工作流引擎在进行安全加固。收到邮件后报销的人开始逐渐增多在接近下班的时候到达顶峰此时报销系统开始出现了故障数据库监控平台一直收到告警短信数据库连接不足出现大量死锁日志显示调用流程引擎接口出现大量超时同时一直提示CannotGetJdbcConnectionException数据库连接池连接占满。在发生故障后我们尝试过杀掉死锁进程也进行过暴力重启只是不到10分钟故障再次出现收到大量电话投诉。最后没办法只能向全员发送停机维护邮件并发送故障报告而后绩效被打了个D惨...。事故原因分析通过对日志的分析我们很容易就可以定位到故障原因就是保存报销单的save()方法而罪魁祸首就是那个Transactional注解。我们知道Transactional 注解是使用 AOP 实现的本质就是在目标方法执行前后进行拦截。在目标方法执行前加入或创建一个事务在执行方法执行后根据实际情况选择提交或是回滚事务。当 Spring 遇到该注解时会自动从数据库连接池中获取 connection并开启事务然后绑定到 ThreadLocal 上对于Transactional注解包裹的整个方法都是使用同一个connection连接。如果我们出现了耗时的操作比如第三方接口调用业务逻辑复杂大批量数据处理等就会导致我们我们占用这个connection的时间会很长数据库连接一直被占用不释放。一旦类似操作过多就会导致数据库连接池耗尽。在一个事务中执行RPC操作导致数据库连接池撑爆属于是典型的长事务问题类似的操作还有在事务中进行大量数据查询业务规则处理等...何为长事务顾名思义就是运行时间比较长长时间未提交的事务也可以称之为大事务。长事务会引发哪些问题长事务引发的常见危害有数据库连接池被占满应用无法获取连接资源容易引发数据库死锁数据库回滚时间长在主从架构中会导致主从延时变大。如何避免长事务既然知道了长事务的危害那如何在开发中避免出现长事务问题呢很明显解决长事务的宗旨就是 对事务方法进行拆分尽量让事务变小变快减小事务的颗粒度。既然提到了事务的颗粒度我们就先回顾一下Spring进行事务管理的方式。声明式事务首先我们要知道通过在方法上使用Transactional注解进行事务管理的操作叫声明式事务 。使用声明式事务的优点 很明显就是使用很简单可以自动帮我们进行事务的开启、提交以及回滚等操作。使用这种方式程序员只需要关注业务逻辑就可以了。声明式事务有一个最大的缺点就是事务的颗粒度是整个方法无法进行精细化控制。与声明式事务对应的就是编程式事务。基于底层的API开发者在代码中手动的管理事务的开启、提交、回滚等操作。在spring项目中可以使用TransactionTemplate类的对象手动控制事务。Autowired
private TransactionTemplate transactionTemplate; ... public void save(RequestBill requestBill) { transactionTemplate.execute(transactionStatus - {requestBillDao.save(requestBill);//保存明细表requestDetailDao.save(requestBill.getDetail());return Boolean.TRUE; });
}使用编程式事务最大的好处就是可以精细化控制事务范围。所以避免长事务最简单的方法就是不要使用声明式事务Transactional而是使用编程式事务手动控制事务范围。有的同学会说Transactional使用这么简单有没有办法既可以使用Transactional又能避免产生长事务那就需要对方法进行拆分将不需要事务管理的逻辑与事务操作分开Service
public class OrderService{public void createOrder(OrderCreateDTO createDTO){query();validate();saveData(createDTO);}//事务操作Transactional(rollbackFor Throwable.class)public void saveData(OrderCreateDTO createDTO){orderDao.insert(createDTO);}
}query()与validate()不需要事务我们将其与事务方法saveData()拆开。当然这种拆分会命中使用Transactional注解时事务不生效的经典场景很多新手非常容易犯这个错误。Transactional注解的声明式事务是通过spring aop起作用的而spring aop需要生成代理对象直接在同一个类中方法调用使用的还是原始对象事务不生效。其他几个常见的事务不生效的场景为“Transactional 应用在非 public 修饰的方法上Transactional 注解属性 propagation 设置错误Transactional 注解属性 rollbackFor 设置错误同一个类中方法调用导致Transactional失效异常被catch捕获导致Transactional失效”正确的拆分方法应该使用下面两种可以将方法放入另一个类如新增 manager层通过spring注入这样符合了在对象之间调用的条件。Service
public class OrderService{Autowiredprivate OrderManager orderManager;public void createOrder(OrderCreateDTO createDTO){query();validate();orderManager.saveData(createDTO);}
}Service
public class OrderManager{Autowiredprivate OrderDao orderDao;Transactional(rollbackFor Throwable.class)public void saveData(OrderCreateDTO createDTO){orderDao.saveData(createDTO);}
}启动类添加EnableAspectJAutoProxy(exposeProxy true)方法内使用AopContext.currentProxy()获得代理类使用事务。SpringBootApplication.javaEnableAspectJAutoProxy(exposeProxy true)
SpringBootApplication
public class SpringBootApplication {}OrderService.javapublic void createOrder(OrderCreateDTO createDTO){OrderService orderService (OrderService)AopContext.currentProxy();orderService.saveData(createDTO);
}小结使用Transactional注解在开发时确实很方便但是稍微不注意就可能出现长事务问题。所以对于复杂业务逻辑我这里更建议你使用编程式事务来管理事务当然如果你非要使用Transactional可以根据上文提到的两种方案进行方法拆分。往期推荐Spring Boot 如何解决多个定时任务阻塞问题Objects.equals有坑Java 18 正式发布默认 UTF-8finalize 被弃用别再乱用了