1、回顾事务
1.1事务四大特征(ACID)
-
原子性(Atomicity):每一个事务都是一个整体,不可再拆分,事务中所有的 SQL 语句要么都执行成功, 要么都失败。
-
一致性(Consistency):事务在执行前数据库的状态与执行后数据库的状态保持一致。如:转账前2个人的总金额是2000,转账后2个人总金额也是 2000。
-
隔离性(Isolation):事务与事务之间不应该相互影响,执行时保持隔离的状态。
-
多个业务可能操纵同一个资源,防止数据损坏
-
-
持久性(Durability):一旦事务执行成功,对数据库的修改是持久的。就算关机,也是保存下来的。
2、spring中的事务配置
2.1声明式事务的两种配置方式
1.配置xml文件
配置声明事事务(开启事务)
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!--<constructor-arg ref="dataSource" />--> <constructor-arg name="dataSource" ref="dataSource" /> </bean>
结合aop实现事务的织入
<!--配置事务通知--> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <!--给哪些事务配置通知--> <tx:method name="add" propagation="REQUIRED"/> <tx:method name="select" read-only="true"/> <tx:method name="*" /> </tx:attributes> </tx:advice>
意思是这个事物advice的管理者为transactionManager,你从配置文件中应该能够找到一个ID为transactionManager的bean,而这个建议中规定了save方法的传输方式为required,也就是说没有sessionfactory的时候会自动建立,有的时候就不会建立了。 当然了这只是规定了一个advice,你还需要指定一个aop:pointcut去引用他,例如
<!--配置事务切入--> <aop:config> <aop:pointcut id="txPointCut" expression="execution(* com.hyqj.dy.mapper.*.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut" </aop:config>
这样这个advice就被联系到了bussinessService这个pointcut上了
在Transactional注解中配置参数的说明
-
propagation:事务传播行为 即表现为,当有事务的方法调用没有执行事务的方法,具体应该如何去处理。
-
isolation: 事务隔离级别
-
timeout:超时时间,事务需要在一定的时间内进行提交,如果不提交进行回滚 默认值是-1 (不超时) 设置时间以秒为单位
-
readOnly:是否只读 默认值是false,如果是true 则表示只能查询,不能修改添加删除。
-
rollbackFor:回滚,设置出现哪些异常进行事务回滚
-
noRollbackFor:不回滚,设置出现哪些异常不进行事务回滚
propagation(事务的传播属性)的七大属性
-
REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。(Sring默认的)
-
SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。supports
-
MANDATORY:支持当前事务,如果当前没有事务,就抛出异常。mandatory
-
REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
-
NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
-
NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
-
NESTED:支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务。nested
2.注解配置
也需要先配置事务声明。
只需在需要spring来帮忙管理事务的方法上加上@Transaction注解就可以了,注解的方式相对来说更简洁一些,都需要开发者自己去进行配置
@Transactional
@Transactional可以作用在接口、类、类方法;
-
作用于类:当把@Transactional 注解放在类上时,表示所有该类的public方法都配置相同的事务属性信息,会导致事务控制的粒度太大,注解参数无法根据每个类方法的实际需求设置;因此,一般@Transactional注解都会直接添加的需要的方法上;
-
作用于方法:当类配置了@Transactional,方法也配置了@Transactional,方法的事务会覆盖类的事务配置信息;
-
作用于接口:不推荐这种使用方法,因为一旦标注在Interface上并且配置了Spring AOP 使用CGLib动态代理,将会导致@Transactional注解失效;
@Transactional默认只能回滚RuntimeException和RuntimeException下面的子类抛出的异常,不能回滚Exception异常;如果需要支持回滚Exception异常,需要显示的指明,如@Transactional(rollbackFor = Exception.class);
2.2、编程式事务
编程式事务主要有两种用法:通过事务管理器PlatformTransactionManager控制事务和通过事务模板TransactionTemplate控制事务;常用的是TransactionTemplate,如下:
private DataSourceTransactionManager transactionManager; // 同类方法调用 使用编程式事务 TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager); transactionTemplate.execute(transactionStatus -> { queryData(); insertData(); return Boolean.TRUE; });
public class UserService { private final PlatformTransactionManager transactionManager; public UserService(PlatformTransactionManager transactionManager) { this.transactionManager = transactionManager; } public void createUser() { TransactionStatus txStatus = transactionManager.getTransaction(new DefaultTransactionDefinition()); try { userMapper.insertUser(user); } catch (Exception e) { transactionManager.rollback(txStatus); throw e; } transactionManager.commit(txStatus); } }
TransactionStatus表示一个事物的状态。接口提供了控制事务执行和查询事务状态的方法。比如当前调用栈中之前已经存在了一个事物,那么就是通过该接口来判断的,TransactionStatus接口可以让事务管理器控制事务的执行,
如果项目中有些业务逻辑比较简单,而且不经常变动,建议使用@Transactional注解开启事务,因为它更简单,开发效率更高,但是千万要小心事务失效的问题;而使用编程式事务有时可以帮我们解决@Transactional注解声明式事务解决不了的问题,如同类调用;二者可以配合使用;