营销网站的功能构成,常州微网站建设,网站建设要学哪种计算机语言,网页设计与网站建设的目的Spring 事务失效场景 文章目录 Spring 事务失效场景前言事务不生效未开启事务事务方法未被Spring管理访问权限问题基于接口的代理源码解读 CGLIB代理 方法用final修饰同一类中的方法调用多线程调用不支持事务 事务不回滚设置错误的事务传播机制捕获了异常手动抛了别的异常自定义…Spring 事务失效场景 文章目录 Spring 事务失效场景前言事务不生效未开启事务事务方法未被Spring管理访问权限问题基于接口的代理源码解读 CGLIB代理 方法用final修饰同一类中的方法调用多线程调用不支持事务 事务不回滚设置错误的事务传播机制捕获了异常手动抛了别的异常自定义了回滚异常事务被手动提交 其它大事务问题缩小事务范围手动提交事务异步处理 事务的性能和并发性 前言 # Spring事务详解 Spring事务是用于解决数据库操作中的一致性和隔离性问题的机制。数据库事务是一组操作要么全部成功执行要么全部回滚以确保数据的完整性和一致性。Spring事务管理的主要目的是确保在多个数据库操作中要么所有操作都成功提交要么所有操作都回滚从而保持数据的一致性。它提供了以下几个方面的解决方案 原子性Atomicity事务要么全部成功执行要么全部回滚确保数据库操作的原子性。 一致性Consistency事务在执行前后数据库的状态应保持一致。如果事务执行失败数据库应该回滚到事务开始之前的状态。 隔离性Isolation事务应该在相互之间隔离以避免并发操作引起的问题。它确保了在并发环境下每个事务都能够独立地执行并且不会相互干扰。 持久性Durability一旦事务提交其结果应该持久保存在数据库中即使发生系统故障或重启。
Spring事务管理通过使用注解或编程方式来定义事务边界它可以应用于各种数据访问技术如JDBC、Hibernate、JPA等。它还提供了不同的传播行为和隔离级别以满足不同的业务需求。通过使用Spring事务开发人员可以简化数据库操作的管理并确保数据的一致性和可靠性从而提高应用程序的可靠性和性能。
事务不生效
未开启事务
如果使用的是springboot项目springboot通过DataSourceTransactionManagerAutoConfiguration类默认开启了事务。只需要配置spring.datasource相关参数即可。 如果使用的是传统的spring项目则需要在applicationContext.xml文件中手动配置事务相关参数。如果忘了配置事务肯定是不会生效的。
事务方法未被Spring管理
未将类标记为 Spring 管理的组件确保类被标记为 Service 、 Component 或其他适当的注解以便 Spring 能够扫描并管理该类。未使用 Transactional 注解标记事务方法确保需要进行事务管理的方法被标记为 Transactional 注解以便 Spring 能够识别并应用事务管理。
访问权限问题
基于接口的代理
当使用基于代理的事务管理时Spring会在运行时生成一个代理对象来管理事务。这个代理对象会拦截被注解的方法并在方法执行前后进行事务的开启、提交或回滚等操作。默认情况下Spring的事务代理是基于接口实现的因此只有public方法才能被代理。如果事务方法是private、protected或默认访问级别的Spring无法生成代理对象从而导致事务不生效。
/*** 不生效*/
Transactional
private void ransactionOne() {User user new User();user.setUsername(张三);userMapper.insertUser(user);methodOne();int a 3 / 0;logger.info(String.valueOf(a));
}源码解读 判断是否是public的 有事务的类
CGLIB代理
如果使用基于类的代理即使用CGLIB代理Spring可以代理非public方法。可以通过配置 proxy-target-class 属性为true来启用基于类的代理。如果使用的是基于类的代理事务方法可以是非public的但需要启用基于类的代理。SpringBoot 事务示例通过在 EnableTransactionManagement 注解上设置 proxyTargetClass 属性为true来启用基于类的代理。
Configuration
EnableTransactionManagement(proxyTargetClass true)
public class AppConfig {// 配置其他的Bean和组件
}使用基于类的代理时非public方法也可以被代理。但需要注意启用基于类的代理可能会带来一些性能开销因此只有在确实需要代理非public方法时才应使用。确保在配置类上添加了 EnableTransactionManagement 注解并根据需要设置 proxyTargetClass 属性就可以在Spring Boot中使用基于类的代理来进行事务管理了。Spring使用CGLIB库的Enhancer类来生成代理对象生成的代理对象中包含EnhancerBySpringCGLIB作为标识符
方法用final修饰
在 Spring 中事务是通过动态代理来实现的。当一个类被代理时Spring 会创建一个代理对象来包装原始对象从而在方法调用前后添加事务处理逻辑。然而对于 final 方法由于无法重写因此无法创建代理对象事务管理器也就无法对其进行事务处理。
Service
public class UserService {Transactionalpublic final void add(UserModel userModel){saveData(userModel);updateData(userModel);}
}同一类中的方法调用
this是被真实对象所以会直接走methodTwo的业务逻辑而不会走切面逻辑所以事务失败。
/*** 同一个类中的方法调用*/
Override
public void transactionSix() {User user new User();user.setUsername(张三);userMapper.insertUser(user);// 没有事务的方法调用有事务的方式相当于使用 this 调用不会走 AOP 的逻辑methodTwo();
}Transactional(rollbackFor Throwable.class)
public void methodTwo() {User user new User();user.setUsername(李四);userMapper.insertUser(user);int a 5 / 0;logger.info(String.valueOf(a));
}解决方法可以是在方法上添加Transactional注解EnableAspectJAutoProxy(exposeProxy true)在启动类中添加会由Cglib代理实现。如果只想让methodTwo的事务生效可以把methodTwo写到一个新的service用service调用
/*** 同一个类中的方法调用调用 service 的方法insetUser 方法事务可以生效*/
Override
public void transactionSeven(){User user new User();user.setUsername(张三);userMapper.insertUser(user);transactionHelpService.insetUser();
}Transactional(rollbackFor Throwable.class)
Override
public void insetUser() {User user new User();user.setUsername(李四);userMapper.insertUser(user);int a 5 / 0;logger.info(String.valueOf(a));
}多线程调用
同一个事务其实是指同一个数据库连接只有拥有同一个数据库连接才能同时提交和回滚。如果在不同的线程拿到的数据库连接肯定是不一样的所以是不同的事务。
不支持事务 数据库本身无法支持事务的场景下例如使用Mysql的MyISAM引擎 在MySQL5.5往后默认采用InnoDB存储引擎在这之前采用MyISAM存储引擎。 InnoDB是MySQL的默认事务型引擎 它被设计用来处理大量的短期(short-lived)事务。可以确保事务的完整提交(Commit)和回滚(Rollback)。 MyISAM提供了大量的特性包括全文索引、压缩、空间函数(GIS)等但MyISAM 不支持事务、行级锁、外键 有一个毫无疑问的缺陷就是崩溃后无法安全恢复。
事务不回滚
设置错误的事务传播机制
Propagation.NEVER这种类型的传播特性不支持事务如果有事务则会抛异常。目前只有这三种传播特性才会创建新事务REQUIREDREQUIRES_NEWNESTED
捕获了异常
在代码中手动try...catch了异常
Transactional
Override
public void testOne() {try {User user new User();user.setUsername(张三);userMapper.insertUser(user);int a 3 / 0;} catch (Exception e) {logger.error(e.getMessage(), e);}
}在Spring Boot中事务是通过AOP面向切面编程机制实现的。当使用 Transactional 注解标记一个方法时Spring会在方法开始前创建一个事务并在方法执行结束后根据方法的执行结果来决定是否提交或回滚事务。如果在方法执行期间发生异常Spring会捕获该异常并将事务标记为“回滚”。这意味着在方法执行结束后Spring会自动回滚该事务并撤销对数据库的任何更改。如果在方法中捕获了异常并处理了它那么Spring就无法感知到该异常并且不会将事务标记为“回滚”。这意味着在方法执行结束后Spring会将事务提交而不是回滚这可能会导致不一致的数据状态。
手动抛了别的异常
spring事务默认情况下只会回滚RuntimeException运行时异常和Error错误对于普通的Exception非运行时异常它不会回滚。如果在执行方法时抛出了 RuntimeException 类型的异常Spring 会认为这个异常是不可恢复的也就是说无法通过异常处理来修复。在这种情况下Spring 会回滚事务撤销之前的所有数据库操作以保证数据的一致性。如果抛出的是非 RuntimeException 类型的异常Spring 会认为这个异常是可以恢复的意味着可以通过异常处理来修复。在这种情况下Spring 不会回滚事务而是允许应用程序继续执行。这个机制的主要目的是保护数据的一致性。在大多数情况下如果发生了非 RuntimeException 类型的异常应用程序可能会尝试通过其他方式来处理异常并继续执行。而如果发生了 RuntimeException 类型的异常应用程序可能无法继续执行因此需要回滚事务以撤销之前的数据库操作。
/*** 在代码中手动抛出别的异常 事务不回滚*/
Override
public void testTwo() throws Exception {try {User user new User();user.setUsername(张三);userMapper.insertUser(user);int a 3 / 0;} catch (Exception e) {logger.error(e.getMessage(), e);throw new Exception(test);}
}自定义了回滚异常 在使用Transactional注解声明事务时有时我们想自定义回滚的异常spring也是支持的。可以通过设置rollbackFor参数 rollbackFor 参数指定了回滚事务异常的类型需要检查抛出的异常是否是指定的异常如果不一致则回滚不了 开发规范中会提示需要指定rollbackFor参数事务默认只会在捕获到未被处理的 RuntimeException 或 Error 时才会回滚。如果你的代码中捕获了异常并进行了处理但没有再次抛出 RuntimeException 或 Error事务将不会回滚。确保在捕获异常时将异常重新抛出或手动触发回滚。
事务被手动提交
如果在代码中手动调用了 commit() 方法来提交事务而没有调用 rollback() 方法来回滚事务事务将不会回滚。确保在需要回滚的情况下正确地调用了回滚方法。
其它
大事务问题
缩小事务范围
将大事务拆分为多个小事务并使用嵌套事务来管理它们
手动提交事务
如果需要更细粒度的控制可以在代码中手动管理事务。使用 TransactionTemplate 类可以手动启动、提交或回滚事务
异步处理
对于非关键的操作你可以考虑使用异步处理来减少事务的时间。将一些长时间运行的操作放在异步任务中这样可以减少事务的持有时间从而减少事务的大小和影响范围。
事务的性能和并发性
大事务可能会导致性能问题和并发性竞争。在设计事务时要考虑事务的持有时间和锁竞争情况。尽量将事务范围缩小到最小避免长时间持有事务锁以提高性能和并发性。