
编程式事务通过编程的方式管理事务这种方式带来了很大的灵活性但很难维护。声明式事务将事务管理代码从业务方法中分离出来通过aop进行封装。Spring声明式事务使得我们无需要去处理获得连接、关闭连接、事务提交和回滚等这些操作。使用Transactional注解开启声明式事务。Transactional相关属性如下属性类型描述valueString可选的限定描述符指定使用的事务管理器propagationenum: Propagation可选的事务传播行为设置isolationenum: Isolation可选的事务隔离级别设置readOnlyboolean读写或只读事务默认读写timeoutint (in seconds granularity)事务超时时间设置rollbackForClass对象数组必须继承自Throwable导致事务回滚的异常类数组rollbackForClassName类名数组必须继承自Throwable导致事务回滚的异常类名字数组noRollbackForClass对象数组必须继承自Throwable不会导致事务回滚的异常类数组noRollbackForClassName类名数组必须继承自Throwable不会导致事务回滚的异常类名字数组说一下 spring 的事务隔离级别Spring的事务隔离级别是指在并发环境下事务之间相互隔离的程度。Spring框架支持多种事务隔离级别可以根据具体的业务需求来选择适合的隔离级别。以下是常见的事务隔离级别DEFAULT默认使用数据库默认的事务隔离级别。通常为数据库的默认隔离级别如Oracle为READ COMMITTEDMySQL为REPEATABLE READ。READ_UNCOMMITTED最低的隔离级别允许读取未提交的数据。事务可以读取其他事务未提交的数据可能会导致脏读、不可重复读和幻读的问题。READ_COMMITTED保证一个事务只能读取到已提交的数据。事务读取的数据是其他事务已经提交的数据避免了脏读的问题。但可能会出现不可重复读和幻读的问题。REPEATABLE_READ保证一个事务在同一个查询中多次读取的数据是一致的。事务期间其他事务对数据的修改不可见避免了脏读和不可重复读的问题。但可能会出现幻读的问题。SERIALIZABLE最高的隔离级别保证事务串行执行避免了脏读、不可重复读和幻读的问题。但会降低并发性能因为事务需要串行执行。通过Transactional注解的isolation属性来指定事务隔离级别。有哪些事务传播行为在TransactionDefinition接口中定义了七个事务传播行为PROPAGATION_REQUIRED如果存在一个事务则支持当前事务。如果没有事务则开启一个新的事务。如果嵌套调用的两个方法都加了事务注解并且运行在相同线程中则这两个方法使用相同的事务中。如果运行在不同线程中则会开启新的事务。PROPAGATION_SUPPORTS如果存在一个事务支持当前事务。如果没有事务则非事务的执行。PROPAGATION_MANDATORY如果已经存在一个事务支持当前事务。如果不存在事务则抛出异常IllegalTransactionStateException。PROPAGATION_REQUIRES_NEW总是开启一个新的事务。需要使用JtaTransactionManager作为事务管理器。PROPAGATION_NOT_SUPPORTED总是非事务地执行并挂起任何存在的事务。需要使用JtaTransactionManager作为事务管理器。PROPAGATION_NEVER总是非事务地执行如果存在一个活动事务则抛出异常。PROPAGATION_NESTED如果一个活动的事务存在则运行在一个嵌套的事务中。如果没有活动事务, 则按PROPAGATION_REQUIRED 属性执行。PROPAGATION_NESTED 与PROPAGATION_REQUIRES_NEW的区别:使用PROPAGATION_REQUIRES_NEW时内层事务与外层事务是两个独立的事务。一旦内层事务进行了提交后外层事务不能对其进行回滚。两个事务互不影响。使用PROPAGATION_NESTED时外层事务的回滚可以引起内层事务的回滚。而内层事务的异常并不会导致外层事务的回滚它是一个真正的嵌套事务。Spring 事务传播行为有什么用?主要作用是定义和管理事务边界尤其是一个事务方法调用另一个事务方法时事务如何传播的问题。它解决了多个事务方法嵌套执行时是否要开启新事务、复用现有事务或者挂起事务等复杂情况。总结用途控制事务的传播和嵌套根据具体业务需求可以指定是否使用现有事务或开启新的事务解决事务的传播问题。确保独立操作的事务隔离某些操作(如日志记录、发送通知)应当独立于主事务执行即使主事务失败这些操作也可以成功完成控制事务的边界和一致性不同的业务场景可能需要不同的事务边界例如强制某个方法必须在事务中执行或者确保某个方法永远不在事务中运行谈谈对Spring事务和AOP底层实现原理的区别Spring的声明式事务其实也是通过AOP的这一套底层实现原理实现的都是通过同一个bean的后置处理器来完成的动态代理创建的只是创建动态代理的匹配方式不一样: 区别就是AOP的增强通常是通过切面切点通知来完成的 在创建bean的时候发现bean和切点表达式匹配就会创建动态代理。 而事务内置一个增强类 在创建bean的时候 一旦发现你的类加了Transactional注解 就会创建动态代理。执行动态代理的增强不一样 在执行AOP的bean时会先执行动态代理的增强类 通过责任链分别按顺序执行通知。在执行事务的bean的时候会先执行动态代理的增强类 在执行目标方法前进行异常捕捉出现异常回滚事务 无异常提交事务。Spring事务在什么情况下会失效应用在非 public 修饰的方法上之所以会失效是因为Transactional 注解依赖于Spring AOP切面来增强事务行为这个 AOP 是通过代理来实现的而无论是JDK动态代理还是CGLIB代理Spring AOP的默认行为都是只代理public方法。被用 final 、static 修饰方法和上边的原因类似被用final、static修饰的方法上加 Transactional 也不会生效。static 静态方法属于类本身的而非实例因此代理机制是无法对静态方法进行代理或拦截的final 修饰的方法不能被子类重写事务相关的逻辑无法插入到 final 方法中代理机制无法对 final 方法进行拦截或增强。同一个类中方法调用比如有一个类Test它的一个方法AA再调用本类的方法B不论方法B是用public还是private修饰但方法A没有声明注解事务而B方法有。则外部调用方法A之后方法B的事务是不会起作用的。那为啥会出现这种情况其实这还是由于使用Spring AOP代理造成的因为只有当事务方法被当前类以外的代码调用时才会由Spring生成的代理对象来管理。但是如果是A声明了事务A的事务是会生效的。Bean 未被 spring 管理上边我们知道 Transactional 注解通过 AOP 来管理事务而 AOP 依赖于代理机制。因此Bean 必须由Spring管理实例要确保为类加上如Controller、Service或Component注解让其被Spring所管理这很容易忽视。异步线程调用如果我们在 testMerge() 方法中使用异步线程执行事务操作通常也是无法成功回滚的来个具体的例子。假设testMerge() 方法在事务中调用了 testA()testA() 方法中开启了事务。接着在 testMerge() 方法中我们通过一个新线程调用了 testB()testB() 中也开启了事务并且在 testB() 中抛出了异常。此时testA() 不会回滚 和 testB() 回滚。testA() 无法回滚是因为没有捕获到新线程中 testB()抛出的异常testB()方法正常回滚。在多线程环境下Spring 的事务管理器不会跨线程传播事务事务的状态如事务是否已开启是存储在线程本地的ThreadLocal来存储和管理事务上下文信息。这意味着每个线程都有一个独立的事务上下文事务信息在不同线程之间不会共享。数据库引擎不支持事务事务能否生效数据库引擎是否支持事务是关键。常用的MySQL数据库默认使用支持事务的innodb引擎。一旦数据库引擎切换成不支持事务的myisam那事务就从根本上失效了。RollbackFor 没设置对比如默认没有任何设置 RuntimeException 或者 Error 才能捕获则方法内抛出 IOException 则不会回滚需要配置 Transactional(rollbackForException.class)。异常被捕获了比如代码抛错但是被 catch 了仅打了 log 没有抛出异常这样事务无法正常获取到错误因此不会回滚。Spring多线程事务 能否保证事务的一致性在多线程环境下Spring事务管理默认情况下无法保证全局事务的一致性。这是因为Spring的本地事务管理是基于线程的每个线程都有自己的独立事务。Spring的事务管理通常将事务信息存储在ThreadLocal中这意味着每个线程只能拥有一个事务。这确保了在单个线程内的数据库操作处于同一个事务中保证了原子性。可以通过如下方案进行解决编程式事务 为了在多线程环境中实现事务一致性您可以使用编程式事务管理。这意味着您需要在代码中显式控制事务的边界和操作确保在适当的时机提交或回滚事务。分布式事务 如果您的应用程序需要跨多个资源例如多个数据库的全局事务一致性那么您可能需要使用分布式事务管理如2PC/3PC TCC等来管理全局事务。这将确保所有参与的资源都处于相同的全局事务中以保证一致性。总之在多线程环境中Spring的本地事务管理需要额外的协调和管理才能实现事务一致性。这可以通过编程式事务、分布式事务管理器或二阶段提交等方式来实现具体取决于您的应用程序需求和复杂性。但在 Seata 框架中事务一致性是通过分布式事务协调器TC来保证的。TC 负责协调分布式事务的各个参与者RM确保它们按照相同的顺序执行事务操作从而保证事务的一致性。 具体来说当一个事务开始时TC 会生成一个全局事务 IDXID并将其传播给所有的 RM。每个 RM 在执行事务操作时都会将自己的操作记录到本地事务日志中并将 XID 和操作记录发送给 TC。TC 会根据 XID 和操作记录协调各个 RM 的执行顺序确保它们按照相同的顺序执行事务操作。如果在执行过程中出现异常TC 会根据事务回滚策略决定是否回滚事务。 通过这种方式Seata 框架可以保证分布式事务的一致性即使在多个节点之间进行事务操作也可以确保数据的一致性和可靠性。了解Transactional(rollbackFor Exception.class)注解了解吗Exception分为运行时异常RuntimeException和非运行时异常。事务管理对于企业应用来说是至关重要的即使出现异常情况它也可以保证数据的一致性。当Transactional注解作用于类上时该类的所有 public 方法将都具有该类型的事务属性同时我们也可以在方法级别使用该标注来覆盖类级别的定义。Transactional注解默认回滚策略是只有在遇到RuntimeException(运行时异常) 或者Error时才会回滚事务而不会回滚Checked Exception受检查异常。这是因为 Spring 认为RuntimeException和 Error 是不可预期的错误而受检异常是可预期的错误可以通过业务逻辑来处理。循环依赖什么是循环依赖循环依赖(Circular Dependency)是指两个或多个模块组件之间相互依赖形成一个闭环。简而言之模块A依赖模块B而模块B又依赖于模块A。这会导依赖链的循环无法确定加载或初始化的顺序。Spring怎么解决循环依赖的问题解决步骤