如何提升网站转化率,免费网站营销计划,什么是自适应网站,一个工厂做网站有什么好处事务管理是应用系统开发中必不可少的一部分。Spring 为事务管理提供了丰富的功能支持。Spring 事务管理分为编程式和声明式的两种方式。编程式事务指的是通过编码方式实现事务#xff1b;声明式事务基于 AOP,将具体业务逻辑与事务处理解耦。声明式事务管理使业务代码逻辑不受污…事务管理是应用系统开发中必不可少的一部分。Spring 为事务管理提供了丰富的功能支持。Spring 事务管理分为编程式和声明式的两种方式。编程式事务指的是通过编码方式实现事务声明式事务基于 AOP,将具体业务逻辑与事务处理解耦。声明式事务管理使业务代码逻辑不受污染, 因此在实际使用中声明式事务用的比较多。声明式事务有两种方式一种是在配置文件xml中做相关的事务规则声明另一种是基于Transactional注解的方式。本文将着重介绍基于Transactional注解的事务管理。
需要明确几点
默认配置下Spring 只会回滚运行时、未检查异常继承自 RuntimeException 的异常或者 Error。参考这里Transactional注解只能应用到 public 方法才有效。参考这里 Method visibility and Transactional
以下的示例使用的是 mybatis所以 spring boot 会自动配置一个DataSourceTransactionManager我们只需在方法或者类加上Transactional注解就自动纳入 Spring 的事务管理了。
1. 简单的使用方法
只需在方法加上Transactional注解就可以了。
如下有一个保存用户的方法加入Transactional注解使用默认配置抛出异常之后事务会自动回滚数据不会插入到数据库。
Transactional Override public void save() { User user new User(“花花”); userMapper.insertSelective(user);
if (true) {throw new RuntimeException(save 抛异常了);
}}
2. Transactional 注解的属性介绍
下面分别介绍一下Transactional的几个属性。
2.1 value 和 transactionManager 属性
它们两个是一样的意思。当配置了多个事务管理器时可以使用该属性指定选择哪个事务管理器。
2.2 propagation 属性
事务的传播行为String默认的事务传播类型为 Propagation.REQUIRED。
可选的值有
Propagation.REQUIRED
如果当前存在事务则加入该事务如果当前不存在事务则创建一个新的事务。
Propagation.SUPPORTS
如果当前存在事务则加入该事务如果当前不存在事务则以非事务的方式继续运行。
Propagation.MANDATORY
如果当前存在事务则加入该事务如果当前不存在事务则抛出异常。
Propagation.REQUIRES_NEW
重新创建一个新的事务如果当前存在事务暂停当前的事务。
Propagation.NOT_SUPPORTED
以非事务的方式运行如果当前存在事务暂停当前的事务。
Propagation.NEVER
以非事务的方式运行如果当前存在事务则抛出异常。
Propagation.NESTED
和 Propagation.REQUIRED 效果一样。
这些概念理解起来实在是有点儿抽象后文会用代码示例解释说明。
2.3 isolation 属性
事务的隔离级别默认值为 Isolation.DEFAULT。
可选的值有
Isolation.DEFAULT
使用底层数据库默认的隔离级别。
Isolation.READ_UNCOMMITTED 未提交读
Isolation.READ_COMMITTED 提交读不可重复读oracle默认的隔离级别 Isolation.REPEATABLE_READ 可重复读mysql默认的隔离级别 Isolation.SERIALIZABLE 可串行化
越往下效率越低
2.4 timeout 属性
事务的超时时间默认值为-1。如果超过该时间限制但事务还没有完成则自动回滚事务。
2.5 readOnly 属性
指定事务是否为只读事务默认值为 false为了忽略那些不需要事务的方法比如读取数据可以设置 read-only 为 true。
2.6 rollbackFor 属性
用于指定能够触发事务回滚的异常类型可以指定多个异常类型。
2.7 noRollbackFor 属性
抛出指定的异常类型不回滚事务也可以指定多个异常类型。
3. Transactional 的 propagation 属性代码示例
比如如下代码save 方法首先调用了 method1 方法然后抛出了异常就会导致事务回滚如下两条数据都不会插入数据库。
Transactional(propagation Propagation.REQUIRED) Override public void save() {
method1();User user new User(许嵩);
userMapper.insertSelective(user);if (true) {throw new RuntimeException(save 抛异常了);
}}
public void method1() { User user new User(“汪苏泷”); userMapper.insertSelective(user); }
现在有需求如下就算 save 方法的后面抛异常了也不能影响 method1 方法的数据插入。或许很多人的想法如下给 method1 页加入一个新的事务这样 method1 就会在这个新的事务中执行原来的事务不会影响到新的事务。比如 method1 方法上面再加入注解 Transactional设置 propagation 属性为 Propagation.REQUIRES_NEW代码如下。
Transactional(propagation Propagation.REQUIRED) Override public void save() {
method1();User user new User(许嵩);
userMapper.insertSelective(user);if (true) {throw new RuntimeException(save 抛异常了);
}}
Transactional(propagation Propagation.REQUIRES_NEW) public void method1() { User user new User(“汪苏泷”); userMapper.insertSelective(user); }
运行之后发现然并卵数据也是没有插入数据库。怎么肥四看起来很不科学。其实两个方法都是处于同一个事务中method1 方法并没有创建一个新的事务。
这就得看看Spring 官方文档了。
In proxy mode (which is the default), only external method calls coming in through the proxy are intercepted. This means that self- invocation, in effect, a method within the target object calling another method of the target object, will not lead to an actual transaction at runtime even if the invoked method is marked with Transactional.
大概意思在默认的代理模式下只有目标方法由外部调用才能被 Spring 的事务拦截器拦截。在同一个类中的两个方法直接调用是不会被 Spring 的事务拦截器拦截就像上面的 save 方法直接调用了同一个类中的 method1方法method1 方法不会被 Spring 的事务拦截器拦截。可以使用 AspectJ 取代 Spring AOP 代理来解决这个问题但是这里暂不讨论。
为了解决这个问题我们可以新建一个类。
Service public class OtherServiceImpl implements OtherService {
Autowired
private UserMapper userMapper;Transactional(propagation Propagation.REQUIRES\_NEW)
Override
public void method1() {User user new User(周杰伦);userMapper.insertSelective(user);
}}
然后在 save 方法中调用 otherService.method1 方法
Autowired private OtherService otherService;
Transactional(propagation Propagation.REQUIRED) Override public void save() {
otherService.method1();User user new User(林俊杰);
userMapper.insertSelective(user);if (true) {throw new RuntimeException(save 抛异常了);
}}
这下otherService.method1 方法的数据插入成功save 方法的数据未插入事务回滚。
从日志可以看出首先创建了 save 方法的事务由于 otherService.method1 方法的 Transactional 的 propagation 属性为 Propagation.REQUIRES_NEW 所以接着暂停了 save 方法的事务重新创建了 otherService.method1 方法的事务接着 otherService.method1 方法的事务提交接着 save 方法的事务回滚。这就印证了只有目标方法由外部调用才能被 Spring 的事务拦截器拦截。 还有几个示例如下
接着把 save 方法的 Transactional 注解去掉otherService.method1 的 Transactional 注解保持不变从日志就可以看出只会创建一个 otherService.method1 方法的事务两条数据都会插入。
Autowired private OtherService otherService;
// Transactional(propagation Propagation.REQUIRED) Override public void save() {
otherService.method1();User user new User(汪苏泷);
userMapper.insertSelective(user);if (true) {throw new RuntimeException(save 抛异常了);
}}
接着把 save 方法的 Transactional 注解去掉save 方法改为调用内部的 method1 方法从日志就可以看出完全没有创建任何事务两条数据都会插入。
// Transactional(propagation Propagation.REQUIRED) Override public void save() {
method1();User user new User(华晨宇);
userMapper.insertSelective(user);if (true) {throw new RuntimeException(save 抛异常了);
}}
Transactional(propagation Propagation.REQUIRES_NEW) public void method1() { User user new User(“许嵩”); userMapper.insertSelective(user); }
这样其他的几个 propagation 属性值也就比较好理解了。
4. Transactional 事务实现机制
在应用系统调用声明了Transactional的目标方法时Spring Framework 默认使用 AOP 代理在代码运行时生成一个代理对象根据Transactional的属性配置信息这个代理对象决定该声明Transactional的目标方法是否由拦截器TransactionInterceptor来使用拦截在TransactionInterceptor拦截时会在目标方法开始执行之前创建并加入事务并执行目标方法的逻辑, 最后根据执行情况是否出现异常利用抽象事务管理器AbstractPlatformTransactionManager操作数据源DataSource提交或回滚事务。
Spring AOP 代理有CglibAopProxy和JdkDynamicAopProxy两种以CglibAopProxy为例对于CglibAopProxy需要调用其内部类的DynamicAdvisedInterceptor的 intercept 方法。对于JdkDynamicAopProxy需要调用其 invoke 方法。
正如上文提到的事务管理的框架是由抽象事务管理器AbstractPlatformTransactionManager来提供的而具体的底层事务处理实现由PlatformTransactionManager的具体实现类来实现如事务管理器DataSourceTransactionManager。不同的事务管理器管理不同的数据资源DataSource比如DataSourceTransactionManager管理JDBC 的 Connection。
5. 源码地址
https://github.com/nextyu/spring-transaction-demo
6. 参考资料
Spring 官方文档Spring boot 官方文档Mybatis资料资料资料