# 事务传播属性
事务传播:就是在多个业务层之间相互调用时传递事务的过程称之为事务传播
将事务对象在业务层之间进行传递的过程
(本来不同的业务层是不同的事务对象,那么我们在一个业务层就不能调用其他业务层的方法了,但是spring框架提供了
事务传播属性,可以在一个事务层调用其他事务层的方法时将事务对象也传递过去,这样即使调用了别的事务层,但是由于
事务对象是一样的,那么这就成为了一个原子操作,这样这两个(多个)事务要么一起成功,要么有一个失败就全部回滚),
这就称之为事务的传播属性。
本来不同的业务层时不能相互调用的,但是sprong框架引入了事务的传播属性,事物的传播属性可以使不同的业务层之间
相互调用,进而达到了不同的业务层可以相互调用的结果。
- propagation:事务传播属性
REQUIRED:需要事务 如果外层没有事务 则开启新的事务 如果外层存在事务,则融入当前事务
SUPPORTS:支持事务 如果外层没有事务 不会开启新的事务 如果外层存在事务,则融入当前事务
REQUIRES_NEW:每次开启新的事务 如果外层存在事务,外层事务挂起,自己开启新的事务执行,执行完成,恢复外部事务继续执行
NOT_SUPPORTED:不支持事务 如果外层存在事务,外层事务挂起,自己以非事务方式执行,执行完成,恢复外部事务执行
NEVER:不能有事务 存在事务报错
MANDATORY:强制事务 没有事务报错
NESTED:嵌套事务 事务之间可以嵌套运行 数据库 oracle mysql 不支持
上面四个属性用的比较多,下面三个属性用的比较少。
- 一般在给只需要给查询方法的propagation属性设置为SUPPORTS即可,增、删、改方法一般都不用额外加
由外层方法把事务对象传给内层业务调用的过程(其实本质传的是connection连接对象),称之为事务的传播。
propagation:事务传播属性
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="save*" propagation="SUPPORTS"/>
<tx:method name="update*" propagation="SUPPORTS"/>
注意:SUPPORTS一般用于查询操作(查询操作一般没有事务配置,事务属性一般设置为SUPPORTS),给find方法配一个SUPPORTS,不是说要给find方法加事务,而是让它去融入事务,支持事务传播。
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="delete*"/>
<tx:method name="find*" propagation="SUPPORTS"/>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRES_NEW"/>
NOT_SUPPORTED
<tx:method name="save*" propagation="NEVER"/>
<tx:method name="update*" propagation="NOT_SUPPORTED"/>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="NEVER"/>
<tx:method name="save*" propagation="MANDATORY"/>
<tx:method name="update*" propagation="NEVER"/>
实际操作:
需要用到的类及结构:
AService接口
public interface AService {
void save();
String find();
}
BService接口
public interface BService {
void update();
String find();
}
BService实现类
public class BServiceImpl implements BService{
@Override
public void update() {
System.out.println("BService Update");
}
@Override
public String find() {
System.out.println("BService Find");
return "BService find";
}
}
AService实现类
public class AServiceImpl implements AService{
private BService bbbService;
public void setBbbService(BService bbbService) {
bbbService.update();
this.bbbService = bbbService;
}
@Override
public void save() {
System.out.println("AService Save");
}
@Override
public String find() {
System.out.println("AService Find");
return "AService find";
}
}
spring.xml配置文件
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/lb?characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
bean>
<bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactory">
<property name="dataSource" ref="dataSource"/>
<property name="mapperLocations" value="classpath:com/baizhi/mapper/*.xml"/>
<property name="typeAliasesPackage" value="com.baizhi.eneity"/>
bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<property name="basePackage" value="com.baizhi.dao"/>
bean>
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
<property name="dataSource" ref="dataSource"/>
bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" propagation="NESTED"/>
<tx:method name="update*" propagation="NEVER"/>
<tx:method name="delete*"/>
<tx:method name="find*" propagation="SUPPORTS"/>
tx:attributes>
tx:advice>
<aop:config>
<aop:pointcut id="pc" expression="execution(* com.baizhi.service.*ServiceImpl.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pc"/>
aop:config>
<bean class="com.baizhi.service.UserServiceImpl" id="userService">
<property name="userDAO" ref="userDAO"/>
bean>
<bean class="com.baizhi.service.BServiceImpl" id="bService">bean>
<bean class="com.baizhi.service.AServiceImpl" id="aService">
<property name="bbbService" ref="bService"/>
bean>
beans>
测试类
public class TestAservice {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
AService aService = (AService) context.getBean("aService");
aService.save();
}
}
REQUIRED:需要事务 如果外层没有事务 则开启新的事务 如果外层存在事务,则融入当前事务
SUPPORTS:支持事务 如果外层没有事务 不会开启新的事务 如果外层存在事务,则融入当前事务
REQUIRES_NEW:每次开启新的事务 如果外层存在事务,外层事务挂起,自己开启新的事务执行,执行完成,恢复外部事务继续执行
NOT_SUPPORTED:不支持事务 如果外层存在事务,外层事务挂起,自己以非事务方式执行,执行完成,恢复外部事务执行
NEVER:不能有事务 存在事务报错
MANDATORY:强制事务 没有事务报错
NESTED:嵌套事务 事务之间可以嵌套运行 数据库 oracle mysql 不支持
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="SUPPORTS"/>
<tx:method name="update*" propagation="SUPPORTS"/>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRES_NEW"/>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="NOT_SUPPORTED"/>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="NEVER"/>
<tx:method name="update*" propagation="MANDATORY"/>
关于事务的隔离级别以及脏读、不可重复读、幻读是什么,建议大家参考一下我以前写的博客,里面有更清晰的详解:
MySQL中事务相关问题的分析
# 事务的隔离级别
isolation :事务隔离级别
DEFAULT: 使用数据库默认的隔离级别 [推荐]
READ_UNCOMMITTED: 读未提交 一个客户端读到了另一个客户端没有提交的数据 脏读现象
READ_COMMITTED : 读提交 一个客户端只能读到另一个客户端提交的数据 避免脏读现象 [oracle默认的隔离级别]
REPEATABLE_READ : 可重复读 主要是用来避免不可重复读实现的 行锁 [mysql默认的隔离级别]
SERIALIZABLE : 序列化读 主要是用来避免幻影读现象的出现 表锁
注意:隔离级别越高,查询效率越低 一般推荐使用数据库默认隔离级别
# tx:method标签的isolation属性
在spring中可以给tx:method标签的isolation属性赋值设置隔离级别
# tx:method标签的read-only属性
read-only:事务读写性 true 只读 不能执行增删改操作 false: 可读可写(mysql支持,oracle不支持)
read-only这里一般都不加
# tx:method标签的rollback-for属性
rollback-for:出现什么类型异常回滚 默认出现RuntimeException(运行时异常)及其子类异常回滚
一般不设置
# tx:method标签的no-rollback-for属性
no-rollback-for:出现什么类型异常不回滚
一般也不会做设置
# tx:method标签的timeout属性
timeout:事务超时性 -1 永不超时(默认)
设置>=0正整数 代表设置超时时间 单位秒 假如设置1秒,1秒还没执行完,直接报错
一般我们也不会修改timeout这个属性
在演示事务传播时经常会出现外部事务挂起,内部事务执行,那外部事务挂起,它挂起多长时间,如果内部事务阻塞,外部事务得一直挂起
如果没有做任何配置,内部事务没有执行完,外部事务就老老实实的等待,永不超时
测试类:
@Test
public void testSave(){
UserService userService = (UserService) context.getBean("userService");
User user = new User();
user.setBir(new Date());
user.setName("小陈");
user.setAge(23);
userService.save(user);
}
<tx:method name="save*" read-only="true"/>
<tx:method name="save*" no-rollback-for="java.lang.RuntimeException"/>