在Web 开发中,式方案Spring 框架已经成为了众多开发者的及其解决首选。Spring 的事务失效声明式事务管理是其中最重要的特性之一,它可以帮助我们简化业务逻辑的式方案复杂度,并且确保在出现异常情况时数据的及其解决一致性。
事务失效情况很常见,事务失效但我们只要注意,式方案就可以避免事情发生!及其解决在本文中,事务失效我将详细地介绍 Spring 声明式事务的式方案源码实现和事务失效常见的五种情况,并给出有效的及其解决解决方案。
其实我们常说的事务失效是声明式事务(@Transactional)的失效,本文也是从声明式事务来进行演示的!
通过本文的学习,你将掌握如何正确地使用 Spring 的事务管理,减少生产事故。
「一定要保持数据一致性」。
我们拿出几个经常使用的参数来简单介绍一下:
声明式事务实现类为:TransactionInterceptor ,下面我们来一起看看这个类!
源码版本为Springboot2.7.1。
public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable{ }
TransactionInterceptor UML图:
声明式事务主要是通过AOP实现,主要包括以下几个节点:
源码:
@Override@Nullablepublic Object invoke(MethodInvocation invocation) throws Throwable { // Work out the target class: may be { @code null}. // The TransactionAttributeSource should be passed the target class // as well as the method, which may be from an interface. Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null); // Adapt to TransactionAspectSupport's invokeWithinTransaction... return invokeWithinTransaction(invocation.getMethod(), targetClass, new CoroutinesInvocationCallback() { @Override @Nullable public Object proceedWithInvocation() throws Throwable { return invocation.proceed(); } @Override public Object getTarget() { return invocation.getThis(); } @Override public Object[] getArguments() { return invocation.getArguments(); } });}
下面是核心处理方法,把不太重要的代码忽略了,留下每一步的节点。
@Nullableprotected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass, final InvocationCallback invocation) throws Throwable { // 获取事务属性 final TransactionManager tm = determineTransactionManager(txAttr); // 准备事务 TransactionInfo txInfo = prepareTransactionInfo(ptm, txAttr, joinpointIdentification, status); // 执行目标方法 Object retVal = invocation.proceedWithInvocation(); // 回滚事务 completeTransactionAfterThrowing(txInfo, ex); // 提交事务 commitTransactionAfterReturning(txInfo);}
下面我们从几个情况来给大家展示失效场景并给出解决方案。
public class UserServiceImpl implements UserService { @Autowired private UserDao userDao; @Override @Transactional(rollbackFor = Exception.class) public void addUser(User user) { userDao.addUser(user); }}
如上代码所示,UserServiceImpl 类没有被声明为 Spring Bean,因此其中的 addUser() 方法无法受到 Spring 事务管理的保护。 我们使用Spring,要把类交给Spring进行管理,不然是无法生效!
「解决方案:」 交给spring进行管理bean,在类上添加:@Service!
@Servicepublic class UserService { @Autowired private UserDao userDao; @Transactional(rollbackFor = Exception.class) protected void addUser(User user) { userDao.addUser(user); }}
我们上面说了声明式事务是基于AOP实现的,AOP是通过代理模式实现的,即为目标对象生成一个代理对象,当调用代理对象的方法时,会自动添加事务的控制代码。 在这种情况下,如果事务注释所在的方法不是public的,则无法生成代理对象,因此事务代码将无法添加到方法执行前后,导致事务失效。
其实这种情况还是不经常这么使用,我们基本都是使用接口和实现大部分都是public修饰的!
「解决方案:」 使用public来修饰方法。
@Servicepublic class UserServiceImpl implements UserService { @Autowired private UserDao userDao; @Override @Transactional(rollbackFor = Exception.class) public void addUser(User user) { try { userDao.addUser(user); } catch (Exception e) { // 处理异常,但没有抛出或重新抛出异常 log.error("add user error", e); } }}
如上代码所示,如果 userDao.addUser() 方法抛出异常,但是在 UserServiceImpl.addUser() 中被捕获并处理了,事务检测不到有异常抛出,那么事务不会回滚。
「解决方案:」 catch 处理完成后,在重新把异常在抛出去:throw e。
@Servicepublic class UserServiceImpl implements UserService { @Autowired private UserDao userDao; @Override public void addUser(User user) { doAddUser(user); } @Transactional(rollbackFor = Exception.class) public void doAddUser(User user) { userDao.addUser(user); }}
Spring使用代理来实现事务控制,但是这种方法直接调用了this对象的方法,则无法通过代理来拦截该方法调用,从而使得事务失效。
推荐使用有两种:
网上还有一些使用AopContext.currentProxy()拿到代理对象的、自己注入自己的、抽到单独的bean里的 这里小编不是很推荐!
方法一完整展示:
如果觉得Service里注入ApplicationContext 不优雅,可以抽到单独的工具bean里!
@Servicepublic class UserServiceImpl implements UserService { @Autowired private UserDao userDao; @Autowired private ApplicationContext applicationContext; @Override public void addUser(User user) { UserServiceImpl userService = applicationContext.getBean(UserServiceImpl.class); userService.doAddUser(user); } @Transactional(rollbackFor = Exception.class) public void doAddUser(User user) { userDao.addUser(user); }}
MyISAM 存储引擎是 MySQL 的一种存储引擎,它是 MySQL 5.1 版本之前的默认存储引擎,它是不支持事务的。从 MySQL 5.5 版本开始,InnoDB 成为了 MySQL 的默认存储引擎。我们想使用也可以切换到MyISAM引擎。
「解决方案:」 把mysql换到5.5以上使用InnoDB 存储引擎。
ALTER TABLE table_name ENGINE = MyISAM;
default-storage-engine=MyISAM
CREATE TABLE table_name ( column1 datatype, column2 datatype, ...) ENGINE = MyISAM;
本文总结了Spring 声明式事务的源码实现、五种常见的事务失效情况,并提供了相应的解决方案。
当然还有很多情况:被final修饰、多线程调用、传播行为使用不当、抛的异常不对应等等
理解 Spring 事务机制的,深入了解 Spring 事务的内部原理。同时,在使用声明式事务的过程中,我们也可以针对自己的业务场景进行定制化的配置,比如指定特定的事务传播机制、设置超时时间等,这些都有助于更好地应对复杂的业务场景和代码需求。这样才能真正地提高系统的可维护性、可扩展性和稳定性。
责任编辑:姜华 来源: 小王博客基地 Spring声明式事务(责任编辑:休闲)
腾讯正式开源图计算框架Plato,十亿级节点图计算进入分钟级时代
运动防抖+全面屏 创维人工智能2.0电视58HD 618特价4599元
国科微(300672.SZ):股东陈岗解除质押245万股 占其所持股份比例22.32%