基础建设的网站有哪些内容,做网站人员配置,网站锚文本怎么做,外贸网站建设书籍所谓事务管理#xff0c;其实就是“按照给定的事务规则来执行提交或者回滚操作”。
Spring 并不直接管理事务#xff0c;而是提供了多种事务管理器#xff0c;他们将事务管理的职责委托给 Hibernate 或者 JTA 等持久化机制所提供的相关平台框架的事务来实现。
Spring 事务…所谓事务管理其实就是“按照给定的事务规则来执行提交或者回滚操作”。
Spring 并不直接管理事务而是提供了多种事务管理器他们将事务管理的职责委托给 Hibernate 或者 JTA 等持久化机制所提供的相关平台框架的事务来实现。
Spring 事务管理器接口 org.springframework.transaction.PlatformTransactionManager 通过这个接口Spring 为各个平台如 JDBC(DataSourceTransactionManager)、Hibernate(HibernateTransactionManager)、JPA(JpaTransactionManager) 等都提供了对应的事务管理器但是具体的实现就是各个平台自己的事情了。
Spring 事务的分类
Spring 提供了两种事务管理方式声明式事务管理和编程式事务管理。对不同的持久层访问技术编程式事务提供一致的事务编程风格通过模板化的操作一致性地管理事务而声明式事务基于 Spring AOP 实现却并不需要程序开发者成为 AOP 专家亦可轻易使用 Spring 的声明式事务管理。
声明式事务
Spring 的声明式事务管理是建立在 Spring AOP 机制之上的其本质是对目标方法前后进行拦截并在目标方法开始之前创建或者加入一个事务在执行完目标方法之后根据执行情况提交或者回滚事务。
简单地说声明式事务是编程式事务 AOP 技术包装使用注解进行扫包指定范围进行事务管理。声明式事务管理要优于编程式事务管理这正是 Spring 倡导的非侵入式的开发方式。
示例
Transactional
public void transactionDemo {// TODO 业务代码
}编程式事务
在 Spring 出现以前编程式事务管理对基于 POJO 的应用来说是唯一选择。我们需要在代码中显式调用 beginTransaction()、commit()、rollback() 等事务管理相关的方法这就是编程式事务管理。
简单地说编程式事务就是在代码中显式调用开启事务、提交事务、回滚事务的相关方法因此代码侵入性较大。
示例
Autowired
private PlatformTransactionManager transactionManager;public void transactionDemo() {TransactionStatus transactionStatus this.transactionManager.getTransaction(new DefaultTransactionDefinition());try {// TODO 业务代码// 提交事务this.transactionManager.commit(transactionStatus);} catch (Exception e) {// 回滚事务this.transactionManager.rollback(transactionStatus);}
}Spring 事务的原理
使用 AOP 环绕通知 和 异常通知。
注意 在使用 Spring 事务时不能使用 try-catch 进行异常捕获要将异常抛给外层使其进行异常拦截触发事务机制。
事务的传播行为
所谓事务的传播行为是指如果在开始当前事务之前一个事务上下文已经存在此时有若干选项可以指定一个事务性方法的执行行为。事务传播行为是为了解决业务层方法之间互相调用的事务问题。
当事务方法被另一个事务方法调用时必须指定事务应该如何传播。例如方法可能继续在现有事务中运行也可能开启一个新事务并在自己的事务中运行。
在 Spring 中有七种事务传播行为 下面我们就来看看吧。
REQUIRED
如果当前存在事务则加入该事务如果当前没有事务则创建一个新的事务。Transactional 注解默认使用就是这个事务传播行为。 也就是说
如果外部方法没有开启事务的话REQUIRED 修饰的内部方法会新开启自己的事务且开启的事务相互独立互不干扰。如果外部方法开启事务并且被 REQUIRED 的话所有 REQUIRED 修饰的内部方法和外部方法均属于同一事务只要一个方法回滚整个事务均回滚。
示例
/*** author star*/
public class ClassA {Transactional(propagation Propagation.REQUIRED)public void methoA() {// TODO 业务代码ClassB classB new ClassB();classB.methodB();}
}/*** author star*/
public class ClassB {Transactional(propagation Propagation.REQUIRED)public void methodB() {// TODO 业务代码}
}使用 REQUIRED 传播行为修饰的 methodA() 和 methodB() 的话两者使用的就是同一个事务只要其中一个方法发生回滚整个事务都回滚。
REQUIRES_NEW
创建一个新的事务如果当前存在事务则把当前事务挂起。也就是说不管外部方法是否开启事务REQUIRES_NEW 修饰的内部方法会开启一个新的事务。如果外部方法开启事务则两个事务互不干扰相互独立并且外部事务会挂起等待内部事务执行完后才继续执行。
示列 /*** author star*/
public class ClassA {Transactional(propagation Propagation.REQUIRED)public void methoA() {// TODO 业务代码ClassB classB new ClassB();classB.methodB();}
}/*** author star*/
public class ClassB {Transactional(propagation Propagation.REQUIRES_NEW)public void methodB() {// TODO 业务代码}
}如果使用 REQUIRED 事务传播行为修饰 methodA()使用 REQUIRES_NEW 修饰 methodB()。那么methodA() 发生异常回滚methodB() 是不会跟着回滚因为 methodB() 开启了独立的事务。但是如果 methodB() 发生异常回滚了并且抛出的异常被 methodA() 的事务管理机制检测到了methodA() 也会回滚。
SUPPORTS
如果当前存在事务则加入该事务如果当前没有事务则以非事务的方式继续运行。这个通常是用来处理那些并非原子性的非核心业务逻辑操作。不常用。
NOT_SUPPORTED
以非事务方式运行如果当前存在事务则把当前事务挂起。它可以帮助将事务极可能的缩小因为一个事务越大它存在的风险也就越多所以在处理事务的过程中要保证尽可能的缩小范围。
示列
/*** author star*/
public class ClassA {Transactional(propagation Propagation.REQUIRED)public void methoA() {// TODO 业务代码ClassB classB new ClassB();classB.methodB();}
}/*** author star*/
public class ClassB {Transactional(propagation Propagation.NOT_SUPPORTED)public void methodB() {// TODO 执行 1000 次非核心业务逻辑}
}假如 methodB() 执行循环 1000 次的非核心业务逻辑操作并且它处在 methodA() 的事务中这样会造成事务太大导致出现一些难以考虑周全的异常情况。所以使用 NOT_SUPPORTED 修饰 methodB()当执行到 methodB() 时将 methodA() 的事务挂起等 methodB() 以非事务的状态运行完成后再继续 methodA() 的事务。
NEVER
以非事务方式运行如果当前存在事务则抛出抛出Runtime 异常强制停止执行。
如果 methodA() 是使用 REQUIRED 修饰的 而methodB() 的是使用 NEVER 修饰的。当执行到 methodB() 时就要抛出异常了。
MANDATORY
如果当前存在事务则加入该事务如果当前没有事务则抛出异常。也就是说MANDATORY 要求上下文中必须要存在事务否则就会抛出异常。
配置 MANDATORY 级别的事务是有效控制上下文调用代码而遗漏添加事务管理的保证手段。比如一段代码不能单独被调用执行但是一旦被调用就必须有事务包含的情况就可以使用 MANDATORY 级别的事务。
NESTED
如果当前存在事务则创建一个事务作为当前事务的嵌套事务来运行如果当前没有事务则该取值等价于 REQUIRED。
也就是说如果外部方法开启事务的话NESTED 修饰的内部方法属于外部事务的子事务外部主事务回滚的话子事务也会回滚而内部子事务可以单独回滚而不影响外部主事务和其他子事务。因为 NESTED 事务它有一个 savepoint在内部方法执行失败后进行回滚外部方法也会回滚到 savepoint 点上。此时如果外部方法将内部方法抛出的异常进行了捕获则会继续往下执行直到完成自己的事务。如果外部方法没有捕获异常则会根据事务规则进行回滚。
如果外部方法未开启事务NESTED 和 REQUIRED 作用相同修饰的内部方法都会新开启自己的事务且开启的事务相互独立互不干扰。
示列
/*** author star*/
public class ClassA {Transactional(propagation Propagation.REQUIRED)public void methoA() {// TODO 业务代码ClassB classB new ClassB();try {// savepointclassB.methodB();} catch (Exception e) {// TODO 执行其他业务}// TODO 业务代码}
}/*** author star*/
public class ClassB {Transactional(propagation Propagation.NESTED)public void methodB() {// TODO 业务代码}
}当 methodB() 执行失败后进行回滚methodA() 也会回滚到 savepoint 点上而 methodA() 捕获了 methodB() 抛出的异常继续执行自己的业务代码。
基于注解 Transactional 声明事务失效分析
在开发过程中可能会遇到使用 Transactional 进行事务管理时出现失效的情况。这里我们的讨论是基于事务的默认传播行为是 REQUIRED。
常见失效场景 如果使用 MySQL 且引擎是 MyISAM则事务会不起作用原因是 MyISAM 不支持事务改成 InnoDB 引擎则支持事务。 注解 Trasactional 只能加在 public 修饰的方法上事务才起效。如果加在 protect、private 等非 public 修饰的方法上事务将失效。 如果在开启了事务的方法内使用了 try-catch 语句块对异常进行了捕获而没有将异常抛到外层事务将不起效。 在不同类之间的方法调用中如果 A 方法开启了事务B 方法没有开启事务B 方法调用了 A 方法。 如果 B 方法中发生异常但不是调用的 A 方法产生的则异常不会使 A 方法的事务回滚此时事务无效。如果 B 方法中发生异常异常是调用的 A 方法产生的则 A 方法的事务回滚此时事务有效。在 B 方法上加上注解 Trasactional这样 A 和 B 方法就在同一个事务里了不管异常产生在哪里事务都是有效的。简单地说不同类之间方法调用时异常发生在无事务的方法中但不是被调用的方法产生的被调用的方法的事务无效。只有异常发生在开启事务的方法内事务才有效。 在同一个类的方法之间调用中如果 A 方法没开启事务B方法开启事务A方法调用B方法是直接调用的不会调用spring生成的开启事务的代理方法。但是如果 A 方法开启 REQUIRED 事务由于事务传播机制B 方法会自动加入到 A 的事务中。 如果使用了 Spring MVC则 context:component-scan 重复扫描问题可能会引起事务失效。
原因分析
在应用系统调用声明 Transactional 的目标方法时Spring Framework 默认使用 AOP 代理在代码运行时生成一个代理对象再由这个代理对象来统一管理。
Spring 事务是使用 AOP 环绕通知和异常通知就是对方法进行拦截在方法执行前开启事务在捕获到异常时进行事务回滚在方法执行完成后提交事务。
最后
Spring 团队建议在具体的类或类的方法上使用 Transactional 注解而不要使用在类所要实现的任何接口上。在接口上使用 Transactional 注解只能当你设置了基于接口的代理时它才生效。因为注解是不能继承的这就意味着如果正在使用基于类的代理时那么事务的设置将不能被基于类的代理所识别而且对象也将不会被事务代理所包装。
Spring 文档中写到Spring AOP 部分使用 JDK 动态代理或者 CGLIB 来为目标对象创建代理如果被代理的目标对象实现了至少一个接口则会使用 JDK 动态代理。所有该目标类型实现的接口都将被代理。若该目标对象没有实现任何接口则创建一个CGLIB代理。
参考
https://juejin.im/post/5b00c52ef265da0b95276091#heading-9
https://blog.csdn.net/rylan11/article/details/76609643
https://blog.csdn.net/justloveyou_/article/details/73733278