spring boot实战之事务
事务在项目里也是不可或缺的一部分,建议形成一个统一的事务管理规范,不要出现让程序员根据业务自行添加,团队成员能力有高有低,很容易就出现需要事务时没添加事务,这种问题又很难测试出来,运行时却会不定时出现数据的不一致。
想实现类似原spring项目里通过aop方式配置事务的效果,各种尝试,目前还未找到十分可行的方式。测试可用的事务配置方式有两种:在service上添加@Transactional注解 和 引入xml配置文件(不推荐),建议使用@Transactional注解来进行事务配置。
1、@Transactional实现事务
spring boot项目内使用事务最简单直接的方式就是在每个service类上添加@Transactional注解,即可自动开启对事务的支持,不需要进行额外操作。
@Transactional(isolation=Isolation.REPEATABLE_READ,propagation=Propagation.REQUIRED)
该注解也可添加在方法上,对事物进行更精细化的管理,注意引用spring包下的,不要引用javax包下的。
2、引入xml配置实现事务
1、 创建com.onecoderspace.transaction.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns:util="http://www.springframework.org/schema/util"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd"
default-lazy-init="false" default-autowire="no">
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager" />
<!-- 对于service使用annotation声明事物 -->
<tx:annotation-driven mode="proxy" proxy-target-class="true" transaction-manager="transactionManager" order="100" />
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="insert*" isolation="REPEATABLE_READ" />
<tx:method name="save*" isolation="REPEATABLE_READ" />
<tx:method name="update*" isolation="REPEATABLE_READ" />
<tx:method name="del*" isolation="REPEATABLE_READ" />
<tx:method name="do*" isolation="REPEATABLE_READ" />
<tx:method name="*" isolation="REPEATABLE_READ" read-only="true" />
</tx:attributes>
</tx:advice>
<aop:config proxy-target-class="true">
<aop:advisor pointcut="execution(* com.onecoderspace..*.service..*.*(..))" advice-ref="txAdvice" order="200" />
</aop:config>
</beans>
2、引入xml配置文件
@Configuration
@ImportResource("classpath:com.onecoderspace.transaction.xml")
public class AopTransactionConfig {
}
3、小结
- service层需要统一添加事务,避免部分人员忘记事务处理,在运行过程中导致数据不一致;
- 在每个service上添加注解实现事务:定好项目整体事务隔离级别和传播属性即可在项目级别形成规范,胜在简单,可执行性更高,目前本公司使用该方式;
- 在service方法上添加注解实现事务:事务管理更灵活,更有针对性,缺点是难以形成统一规范,依赖编程人员的经验和能力,如果团队内存在经验不很充足的成员不推荐使用该方式;
- 通过引入xml配置实现事务:配置简单,在xml内一处配置即可实现事务管理,耦合性更低;缺点就是使用了xml配置,因此不太推荐使用该方式;
- 隔离级别(isolation)通常选择REPEATABLE_READ;传播属性通常使用默认值(REQUIRED)即可。
4、扩展阅读
隔离级别(isolation)
隔离级别是指若干个并发的事务之间的隔离程度,与我们开发时候主要相关的场景包括:脏读取、重复读、幻读。
我们可以看 org.springframework.transaction.annotation.Isolation 枚举类中定义了五个表示隔离级别的值:
public enum Isolation {
DEFAULT(-1),
READ_UNCOMMITTED(1),
READ_COMMITTED(2),
REPEATABLE_READ(4),
SERIALIZABLE(8);
}
- DEFAULT :这是默认值,表示使用底层数据库的默认隔离级别。对大部分数据库而言,通常这值就是: READ_COMMITTED,mysql5.6默认是REPEATABLE-READ,可以通过select @@tx_isolation查看。
- READ_UNCOMMITTED :该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读和不可重复读,因此很少使用该隔离级别。
- READ_COMMITTED :该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值。
- REPEATABLE_READ :该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。即使在多次查询之间有新增的数据满足该查询,这些新增的记录也会被忽略。该级别可以防止脏读和不可重复读。
- SERIALIZABLE :所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
传播属性(propagation)
所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。
我们可以看 org.springframework.transaction.annotation.Propagation 枚举类中定义了6个表示传播行为的枚举值:
public enum Propagation {
REQUIRED(0),
SUPPORTS(1),
MANDATORY(2),
REQUIRES_NEW(3),
NOT_SUPPORTED(4),
NEVER(5),
NESTED(6);
}
- REQUIRED :如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
- SUPPORTS :如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
- MANDATORY :如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
- REQUIRES_NEW :创建一个新的事务,如果当前存在事务,则把当前事务挂起。
- NOT_SUPPORTED :以非事务方式运行,如果当前存在事务,则把当前事务挂起。
- NEVER :以非事务方式运行,如果当前存在事务,则抛出异常。
- NESTED :如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于 REQUIRED 。
如有更好实现方式,敬请指出。
《spring boot项目实战》之事务使用,本人搭建好的spring boot web后端开发框架已上传至GitHub,欢迎吐槽!
https://github.com/q7322068/rest-base,已用于多个正式项目,当前可能因为版本问题不是很完善,后续持续优化,希望你能有所收获!