SpringJDBC中的事务
Spring中事务分为三种类型 :
- 编程式事务
- 声明式事务
- 注解形式事务
一、编程式事务
在需要执行事务的类中,声明一个DataSourceTransactionManager事务管理器transactionManager,在需要使用事务的方法中要做三个步骤 :
- 定义一个DefaultTransactionDefinition事务默认的标准配置definition,其父类为TransactionDefinition;
- 开始一个事务,执行transactionManager.getTransaction(definition)返回TransactionStatus事务状态status;
- 把要用到事务的部分用try-catch包裹起来,若执行成功则 执行transactionManager.commit(status)进行事务提交,若出现异常则执行transactionManager.rollback(status)进行事务回滚,异常可根据具体业务需求选择是否抛出。
二、声明式事务
声明式事务主要依赖aop进行实现,首先要在applicationContext.xml中引入对应的命名空间。然后进行事务通知配置,决定哪些方法使用事务,哪些方法不使用事务,如下:
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- 目标方法名为batchImport时,启用声明式事务,成功提交,运行时异常回滚 -->
<tx:method name="batchImport" propagation="REQUIRED"/>
<tx:method name="batch*" propagation="REQUIRED"/>
<!-- 设置所有findXXX方法不需要使用事务 -->
<tx:method name="find*" propagation="NOT_SUPPORTED" read-only="true"/>
<tx:method name="get*" propagation="NOT_SUPPORTED" read-only="true"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
然后通过aop定义声明式事务的作用范围,如下:
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* com.flagship..*Service.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
</aop:config>
三、注解形式事务
使用@Transactional注解,@Transactional可以放在类上,也可以放在方法上,放在类上则整个类中的方法都使用该事务,放在方法上则作用在对应的方法上,当类和方法上都存在@Transactional时,执行方法上的@Transactional。@Transactional存在几个属性:
- readOnly:该属性用于设置当前事务是否为只读事务,设置为true表示只读,false则表示可读写,默认值为false。
- rollbackFor:该属性用于设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚。
- rollbackForClassName:该属性用于设置需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,则进行事务回滚。
- noRollbackFor:该属性用于设置不需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,不进行事务回滚。
- noRollbackForClassName:该属性用于设置不需要进行回滚的异常类名称数组。
- timeout:该属性用于设置事务的超时秒数,默认值为-1表示永不超时。
- propagation:该属性用于设置事务的传播行为。
事务传播行为:
REQUIRED:没有事务就创建一个事务,如果外围有一个事务,则嵌套到 外围的事务中,即当A方法有事务时,A当中执行了BC两个方法,他们都各自有事务,当执行完B时,因为事务被嵌套到了A的事务当中,所以B的事务没有提交。当此时出现了异常,整个A过程事务所包裹的方法都会被回滚 ,即BC当中的事务都不会生效,因为他们都被嵌套进了A的事务当中。
REQUIRED_NEW:与REQUIRED相似,没有事务就创建一个事务,如果外围有一个事务,则先将外围的事务挂起,即事务之间是独立的,不会存在嵌套。
NOT_SUPPORTED:以非事务方式执行。如有A方法使用了事务,A方法中调用了B方法,如果B方法没有设置NOT_SUPPORTED,则B方法出现异常会导致A方法的进行事务回滚,如果有设置NOT_SUPPORTED,则B方法出现异常时不会导致A方法发生事务回滚。
SUPPORTS:如果外层有事务,则使用外层的事务,如果没有则按非事务方式执行(有点随大流的意思)。
MANDATORY:如果外层没有事务,则抛出异常。
NEVER:以非事务方式执行,如果外层有事务,则抛出异常。
NESTED:与REQUIRED相似。假设A方法有事务,然后调用了B方法,如果B方法不是使用了NESTED,那么当B方法出现异常时,即使在A方法中调用B方法的地方进行了try-catch且异常不抛出,也依然会导致A方法的整个事务进行回滚。但如果B方法使用了NESTED,那么即便是在B方法中出现了异常,只要在A的try-catch调用B部分的地方不把异常抛出,则不会导致事务回滚。即NESTED可以不影响外围的事务。
事务隔离级别
- @Transactional(isolation = Isolation.READ_UNCOMMITTED)读取未提交数据(会出现脏读, 不可重复读) 基本不使用
- @Transactional(isolation = Isolation.READ_COMMITTED)读取已提交数据(会出现不可重复读和幻读)
- @Transactional(isolation = Isolation.REPEATABLE_READ)可重复读(会出现幻读)
- @Transactional(isolation = Isolation.SERIALIZABLE)串行化
什么是脏读、幻读、不可重复读?
- 脏读 : 一个事务读取到另一事务未提交的更新数据。
- 不可重复读 : 在同一事务中, 多次读取同一数据返回的结果有所不同, 换句话说, 后续读取可以读到另一事务已提交的更新数据. 相反, "可重复读"在同一事务中多次读取数据时, 能够保证所读数据一样, 也就是后续读取不能读到另一事务已提交的更新数据。
- 幻读 : 一个事务读到另一个事务已提交的insert数据。