本文主要介绍Spring的实现方式、隔离级别、传播机制以及事务何时失效。
1.事务的实现方式 spring事务的实现方式有两种,编程式事务和声明式事务,编程式事务要手动管理事务,实际开发当中基本不会使用。本文主要重点介绍声明式事务,声明式事务管理有三种实现方式:基于TransactionProxyFactoryBean的方式、基于AspectJ的XML方式、基于注解的方式。下面主要介绍常用的两种 基于AspectJ的XML方式 在spring核心配置文件中添加事务管理器的配置、事务的增强以及切面
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!--配置事务的通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="transfer*" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>
<!--配置切面-->
<aop:config>
<aop:pointcut id="pointcut1" expression="execution(* com.tx.service.impl.*ServiceImpl.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1" />
</aop:config>
基于注解的方式 在spring核心配置文件中添加事务管理器的配置和开启事务注解
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!--开启事务注解-->
<tx:annotation-driven transaction-manager="transactionManager" />
在事务方法中添加@Transaction注解
@Transactional
public void transferMoney(String source, String destination, Long amount) {
transferDao.payMoney(source, amount);
int i = 100/0;
transferDao.collectMoney(destination, amount);
}
当然,如果是使用springboot,只需要在启动类类上添加@EnableTransactionManagement注解,开启事务支持,在事务方法中添加@Transaction注解就可以使用了。
在一个方法上面添加@Transactional注解之后,spring会基于这个类生成一个代理对象,(如果一个类当中所有的方法都没有加@Transactional注解,那么这个代理对象是不会生成的), 当使用代理对象的方法时,如果方法上存在@Transactional注解,那么代理逻辑会把事务的自动提交设置为fasle,然后再去执行原本的逻辑代码,如果执行逻辑代码没有出现异常,那么代理逻辑中就会将事务进行提交,如果执行业务逻辑方法出现了异常,那么则会将事务进行回滚。 当然,针对哪些异常回滚事务是可以配置的,可以利用@Transaction注解中的rollbackFor属性进行配置,默认情况下会对RuntimeException和Error进行回滚。 总而言之,@Transactional注解方式的事务,使用到了代理模式,代理模式参考
2.事务的隔离级别
Spring事务隔离级别比数据库事务隔离级别多一个default
-
DEFAULT (默认) 这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别。另外四个与JDBC的隔离级别相对应。 -
READ_UNCOMMITTED (读未提交) 这是事务最低的隔离级别,它允许另外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读。 -
READ_COMMITTED (读已提交) 保证一个事务修改的数据提交后才能被另外一个事务读取,另外一个事务不能读取该事务未提交的数据。这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻像读。 -
REPEATABLE_READ (可重复读) 这种事务隔离级别可以防止脏读、不可重复读,但是可能出现幻像读。它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了不可重复读。 -
SERIALIZABLE(串行化) 这是花费最高代价但是最可靠的事务隔离级别,事务被处理为顺序执行。除了防止脏读、不可重复读外,还避免了幻像读。
3.事务的传播机制
- REQUIRED:默认的传播机制,如果当前不存在事务,则创建一个事务,如果存在事务,则加入事务
- SUPPORTS:如果当前不存在事务,则以非事务方式运行,如果存在,则加入
- MANDOTOEY:如果当前不存在事务,抛出异常,如果存在,就加入
- REQUIRED_NEW:如果当前不存在事务,就创建,如果存在,就挂起
- NOT_SUPPORTED:如果当前不存在事务,就以非事务方式运行,如果存在,就挂起当前事务
- NEVER:以非事务方式运行,如果当前存在事务,就抛出异常
- NESTED:如果当前不存在事务,就创建一个事务,如果当前存在,就在嵌套事务中执行
上面的描述想必大家都知道,那么该怎么理解呢? 举个例子:方法A 调用 方法B 上面所说的当前是描述方法A的,上面那7个属性是修饰方法B的
上面代码可以看出,方法A不存在事务,即是当前不存在事务,方法B又是默认的传播机制,所以方法A会创建一个事务,在事务当中运行。其它的以此类推。
为什么本类之间的方法调用不用this,而使用当前类对象呢?因为使用this,事务会失效,因为事务底层使用的是代理模式,this是代理模式的真实对象,testService才是代理对象。
3.spring事务什么时候会失效?
- 同一个类当中的方法使用this调用会失效,因为事务是spring帮我们在底层生成了一个代理对象,如果是this的话,使用的是当前对象,而不是代理对象
- 方法不是public,因为事务的设计就是被外部调用的,不是public就不行
- 数据库不支持事务,如果数据库都不支持事务,那么spring再牛逼也做不了事务,像如果mysql使用的是myISIM引擎,它就不支持事务,这时候事务也会失效
- 异常被吃掉,异常被try{} catch了,事务也是会失效的
- 没有被spring管理
|