文章目录
spring事务分为声明式事务和编程式事务,而声明式事务又分为注解和xml,一般开发都有声明式事务注解。
一、事务相关配置
1、配置连接池:
配置连接池文件
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///spring_day02
jdbc.username=root
jdbc.password=123
<!-- 引入外部属性文件: -->
<bean id="dataSource" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:jdbc.properties"/>
</bean>
<!-- 引入外部属性文件: -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 配置 JDBC 模板 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
<!-- <property name="location" value="classpath:jdbc.properties"/> -->
</bean>
2、xml配置事务
注意:基于xml实现的声明式事务,必须引入aspectJ的依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.1</version>
</dependency>
<!-- 事务管理器,即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>
<!--isolation="DEFAULT" 隔离级别
propagation="REQUIRED" 传播行为
read-only="false" 只读
timeout="-1" 过期时间
rollback-for="" -Exception
no-rollback-for="" +Exception -->
<tx:method name="transfer" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut expression="execution(*cn.zixue.transaction.demo2.AccountServiceImpl.transfer(..))" id="pointcut1"/>
<!-- 由于spring提供了事务的切面,不需要编写,直接配置应用通知就可以使用 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1"/>
</aop:config>
3、注解事务
<!--开启事务的注解驱动 通过注解@Transactional所标识的方法或标识的类中所有的方法(即切点),都会被事务管理器管理事务-->
<!-- transaction-manager属性的默认值是transactionManager,如果事务管理器bean的id正好就是这个默认值,则可以省略这个属性 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- 事务管理器,spring提供的切面 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
注解事务类
//类级的注解、适用于类中所有的public的方法,要是只在接口上写, 接口的实现类就会继承下来、接口的实现类的具体方法,可以覆盖类声明处的设置
@Transactional
public class TestServiceBean implements TestService {
private TestDao dao;
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ,readOnly=false)
public void setDao(TestDao dao) {
this.dao = dao;
}
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public List<Object> getAll() {
return null;
}
}
二、@Transactional注解事务详解
1、@Transactional注解中常用参数说明
参 数 名 称 | 功 能 描 述 |
---|---|
readOnly | 该属性用于设置当前事务是否为只读事务,设置为true表示只读,false则表示可读写,默认值为false。例如:@Transactional(readOnly=true) |
rollbackFor | 该属性用于设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚。例如:指定单一异常类:@Transactional(rollbackFor=RuntimeException.class)指定多个异常类:@Transactional(rollbackFor={RuntimeException.class, Exception.class}),默认运行时异常回滚 。 |
rollbackForClassName | 该属性用于设置需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,则进行事务回滚。例如:指定单一异常类名称:@Transactional(rollbackForClassName=“RuntimeException”)指定多个异常类名称:@Transactional(rollbackForClassName={“RuntimeException”,“Exception”}) |
noRollbackFor | 该属性用于设置不需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,不进行事务回滚。例如:指定单一异常类:@Transactional(noRollbackFor=RuntimeException.class)指定多个异常类:@Transactional(noRollbackFor={RuntimeException.class, Exception.class}) |
noRollbackForClassName | 该属性用于设置不需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,不进行事务回滚。例如:指定单一异常类名称:@Transactional(noRollbackForClassName=“RuntimeException”)指定多个异常类名称:@Transactional(noRollbackForClassName={“RuntimeException”,“Exception”}) |
propagation | 该属性用于设置事务的传播行为。例如:@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true) |
isolation | 该属性用于设置底层数据库的事务隔离级别,事务隔离级别用于处理多事务并发的情况,通常使用数据库的默认隔离级别即可,基本不需要进行设置 |
timeout | 该属性用于设置事务的超时秒数,默认值为-1表示永不超时 |
三、不同传播行为
1、传播方式总结
/**
* 事务传播 - Propagation
* REQUIRED: 使用当前的事务,如果当前没有事务,则自己新建一个事务,子方法是必须运行在一个事务中的;
* 如果当前存在事务,则加入这个事务,成为一个整体。
* 举例:领导没饭吃,我有钱,我会自己买了自己吃;领导有的吃,会分给你一起吃。
* SUPPORTS: 如果当前有事务,则使用事务;如果当前没有事务,则不使用事务。
* 举例:领导没饭吃,我也没饭吃;领导有饭吃,我也有饭吃。
* MANDATORY: 该传播属性强制必须存在一个事务,如果不存在,则抛出异常
* 举例:领导必须管饭,不管饭没饭吃,我就不乐意了,就不干了(抛出异常)
* REQUIRES_NEW: 如果当前有事务,则挂起该事务,并且自己创建一个新的事务给自己使用;
* 如果当前没有事务,则同 REQUIRED
* 举例:领导有饭吃,我偏不要,我自己买了自己吃
* NOT_SUPPORTED: 如果当前有事务,则把事务挂起,自己不适用事务去运行数据库操作
* 举例:领导有饭吃,分一点给你,我太忙了,放一边,我不吃
* NEVER: 如果当前有事务存在,则抛出异常
* 举例:领导有饭给你吃,我不想吃,我热爱工作,我抛出异常
* NESTED: 如果当前有事务,则开启子事务(嵌套事务),嵌套事务是独立提交或者回滚;
* 如果当前没有事务,则同 REQUIRED。
* 但是如果主事务提交,则会携带子事务一起提交。
* 如果主事务回滚,则子事务会一起回滚。相反,子事务异常,则父事务可以回滚或不回滚。
* 举例:领导决策不对,老板怪罪,领导带着小弟一同受罪。小弟出了差错,领导可以推卸责任。
*/
2、传播方式 REQUIRED默认事务
经常用于增删改操作
①、不使用事务
异常之前都直接提交,异常之后都不执行
@Service
public class TestTransServiceImpl implements TestTransService {
@Autowired
private StuService stuService;
@Override
public void testPropagationTrans() {
stuService.saveParent();
stuService.saveChildren();
}
}
@Service
public class StuServiceImpl implements StuService {
@Autowired
private StuMapper stuMapper;
public void saveParent() {//父方法直接调用,直接插入
Stu stu = new Stu();
stu.setName("parent");
stu.setAge(19);
stuMapper.insert(stu);
}
public void saveChildren() {//child1方法没有回滚
saveChild1();
int a = 1 / 0; //异常点 java.lang.ArithmeticException: / by zero
saveChild2(); //由于前面异常不在往后执行,也不回滚前面代码
}
public void saveChild1() {
Stu stu1 = new Stu();
stu1.setName("child-1");
stu1.setAge(11);
stuMapper.insert(stu1);
}
public void saveChild2() {
Stu stu2 = new Stu();
stu2.setName("child-2");
stu2.setAge(22);
stuMapper.insert(stu2);
}
}
运行数据库结果:
id | name | age |
---|---|---|
2 | parent | 19 |
3 | child-1 | 11 |
②、外层使用默认事务
默认事务是会传播,是一个整体事务,子方法出现异常将整体回滚。
@Transactional(propagation = Propagation.REQUIRED)
@Override
public void testPropagationTrans() {
stuService.saveParent();
// int a = 1 / 0; 这里出现异常也一样全部回滚
stuService.saveChildren();
}
public void saveParent() {
Stu stu = new Stu();
stu.setName("parent");
stu.setAge(19);
stuMapper.insert(stu);
}
public void saveChildren() {
saveChild1();
int a = 1 / 0; //异常点 java.lang.ArithmeticException: / by zero
saveChild2();
}
public void saveChild1() {
Stu stu1 = new Stu();
stu1.setName("child-1");
stu1.setAge(11);
stuMapper.insert(stu1);
}
public void saveChild2() {
Stu stu2 = new Stu();
stu2.setName("child-2");
stu2.setAge(22);
stuMapper.insert(stu2);
}
运行结果:没有插入任何数据
③、父方法不开启事务,子方法开启事务
只有子级有事务回滚,其他以非事务方式运行。
@Override
public void testPropagationTrans() {
stuService.saveParent(); //以非事务方式运行
stuService.saveChildren(); //新的整体事务运行
}
public void saveParent() {
Stu stu = new Stu();
stu.setName("parent");
stu.setAge(19);
stuMapper.insert(stu);
}
//由于父级没有事务,所以这里创建一个新事务运行
@Transactional(propagation = Propagation.REQUIRED)
public void saveChildren() {
saveChild1();
int a = 1 / 0; //异常点 java.lang.ArithmeticException: / by zero
saveChild2();
}
public void saveChild1() {
Stu stu1 = new Stu();
stu1.setName("child-1");
stu1.setAge(11);
stuMapper.insert(stu1);
}
public void saveChild2() {
Stu stu2 = new Stu();
stu2.setName("child-2");
stu2.setAge(22);
stuMapper.insert(stu2);
}
运行数据库结果:
id | name | age |
---|---|---|
11 | parent | 19 |
④、父方法开启事务,同时子方法开启事务
子级加入父级事务,属于同一事务,一起回滚或提交
@Transactional(propagation = Propagation.REQUIRED)
@Override
public void testPropagationTrans() {
stuService.saveParent();
stuService.saveChildren();
}
public void saveParent() {
Stu stu = new Stu();
stu.setName("parent");
stu.setAge(19);
stuMapper.insert(stu);
}
@Transactional(propagation = Propagation.REQUIRED)
public void saveChildren() {
saveChild1();
int a = 1 / 0; //异常点 java.lang.ArithmeticException: / by zero
saveChild2();
}
public void saveChild1() {
Stu stu1 = new Stu();
stu1.setName("child-1");
stu1.setAge(11);
stuMapper.insert(stu1);
}
public void saveChild2() {
Stu stu2 = new Stu();
stu2.setName("child-2");
stu2.setAge(22);
stuMapper.insert(stu2);
}
运行数据库结果: 空,没有插入任何数据。
⑤、外层开启默认事务,里层开启默认事务
外层事务和子级事务属于同一事务,所以全部回滚
@Transactional(propagation = Propagation.REQUIRED)
@Override
public void testPropagationTrans() {
stuService.saveParent();
stuService.saveChildren();
int a = 1 / 0;
}
public void saveParent() {
Stu stu = new Stu();
stu.setName("parent");
stu.setAge(19);
stuMapper.insert(stu);
}
//开启默认事务
@Transactional(propagation = Propagation.REQUIRED)
public void saveChildren() {
saveChild1();
//int a = 1 / 0; //异常点 java.lang.ArithmeticException: / by zero
saveChild2();
}
public void saveChild1() {
Stu stu1 = new Stu();
stu1.setName("child-1");
stu1.setAge(11);
stuMapper.insert(stu1);
}
public void saveChild2() {
Stu stu2 = new Stu();
stu2.setName("child-2");
stu2.setAge(22);
stuMapper.insert(stu2);
}
运行数据库结果:空,没有插入任何数据。
3、传播方式 SUPPORTS支持事务
经常用于查询操作
①、外层不开启事务,子级开启支持事务
子级事务与外层保持一致,非事务方式运行。
@Override
public void testPropagationTrans() {
stuService.saveParent();
stuService.saveChildren();
}
public void saveParent() {
Stu stu = new Stu();
stu.setName("parent");
stu.setAge(19);
stuMapper.insert(stu);
}
@Transactional(propagation = Propagation.SUPPORTS)
public void saveChildren() {
saveChild1();
int a = 1 / 0; //异常点 java.lang.ArithmeticException: / by zero
saveChild2();
}
public void saveChild1() {
Stu stu1 = new Stu();
stu1.setName("child-1");
stu1.setAge(11);
stuMapper.insert(stu1);
}
public void saveChild2() {
Stu stu2 = new Stu();
stu2.setName("child-2");
stu2.setAge(22);
stuMapper.insert(stu2);
}
运行数据库结果:
id | name | age |
---|---|---|
15 | parent | 19 |
16 | child-1 | 11 |
②、外层开启默认事务,里层开启支持事务
子级事务与外层保持一致,以事务方式运行。
@Transactional(propagation = Propagation.REQUIRED)
@Override
public void testPropagationTrans() {
stuService.saveParent();
stuService.saveChildren();
}
public void saveParent() {
Stu stu = new Stu();
stu.setName("parent");
stu.setAge(19);
stuMapper.insert(stu);
}
@Transactional(propagation = Propagation.SUPPORTS)
public void saveChildren() {
saveChild1();
int a = 1 / 0; //异常点 java.lang.ArithmeticException: / by zero
saveChild2();
}
public void saveChild1() {
Stu stu1 = new Stu();
stu1.setName("child-1");
stu1.setAge(11);
stuMapper.insert(stu1);
}
public void saveChild2() {
Stu stu2 = new Stu();
stu2.setName("child-2");
stu2.setAge(22);
stuMapper.insert(stu2);
}
运行数据库结果:空,没有插入任何数据。
4、传播方式 MANDATORY 强制事务
①、外层开启支持事务或者无事务,里层开启强制事务
外层必须开启事务方式,不然抛出异常。
org.springframework.transaction.IllegalTransactionStateException:
No existing transaction found for transaction marked with propagation 'mandatory'
//外层开启支持事务或者无事务
@Transactional(propagation = Propagation.SUPPORTS)
@Override
public void testPropagationTrans() {
stuService.saveParent();
stuService.saveChildren();
}
public void saveParent() {
Stu stu = new Stu();
stu.setName("parent");
stu.setAge(19);
stuMapper.insert(stu);
}
@Transactional(propagation = Propagation.MANDATORY)
public void saveChildren() {
saveChild1();
int a = 1 / 0; //异常点 java.lang.ArithmeticException: / by zero
saveChild2();
}
public void saveChild1() {
Stu stu1 = new Stu();
stu1.setName("child-1");
stu1.setAge(11);
stuMapper.insert(stu1);
}
public void saveChild2() {
Stu stu2 = new Stu();
stu2.setName("child-2");
stu2.setAge(22);
stuMapper.insert(stu2);
}
运行数据库结果:
id | name | age |
---|---|---|
43 | parent | 19 |
②、外层开启默认事务,里层开启强制事务
子级事务与外层保持一致,以事务方式运行。
@Transactional(propagation = Propagation.REQUIRED)
@Override
public void testPropagationTrans() {
stuService.saveParent();
stuService.saveChildren();
}
public void saveParent() {
Stu stu = new Stu();
stu.setName("parent");
stu.setAge(19);
stuMapper.insert(stu);
}
@Transactional(propagation = Propagation.MANDATORY)
public void saveChildren() {
saveChild1();
int a = 1 / 0; //异常点 java.lang.ArithmeticException: / by zero
saveChild2();
}
public void saveChild1() {
Stu stu1 = new Stu();
stu1.setName("child-1");
stu1.setAge(11);
stuMapper.insert(stu1);
}
public void saveChild2() {
Stu stu2 = new Stu();
stu2.setName("child-2");
stu2.setAge(22);
stuMapper.insert(stu2);
}
运行数据库结果:空,没有插入任何数据。
5、传播方式 REQUIRES_NEW新建事务
①、外层不开启事务,里层开启新建事务
外层以非事务方式运行,saveChildren新建一个事务
@Override
public void testPropagationTrans() {
stuService.saveParent();
stuService.saveChildren();
}
public void saveParent() {
Stu stu = new Stu();
stu.setName("parent");
stu.setAge(19);
stuMapper.insert(stu);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveChildren() {
saveChild1();
int a = 1 / 0; //异常点 java.lang.ArithmeticException: / by zero
saveChild2();
}
public void saveChild1() {
Stu stu1 = new Stu();
stu1.setName("child-1");
stu1.setAge(11);
stuMapper.insert(stu1);
}
public void saveChild2() {
Stu stu2 = new Stu();
stu2.setName("child-2");
stu2.setAge(22);
stuMapper.insert(stu2);
}
运行数据库结果:
id | name | age |
---|---|---|
23 | parent | 19 |
②、外层开启默认事务,里层开启新建事务
外层以事务方式运行,saveChildren新建一个事务出现异常,影响外层事务回滚。
@Transactional(propagation = Propagation.REQUIRED)
@Override
public void testPropagationTrans() {
stuService.saveParent();
stuService.saveChildren();
}
public void saveParent() {
Stu stu = new Stu();
stu.setName("parent");
stu.setAge(19);
stuMapper.insert(stu);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveChildren() {
saveChild1();
int a = 1 / 0; //异常点 java.lang.ArithmeticException: / by zero
saveChild2();
}
public void saveChild1() {
Stu stu1 = new Stu();
stu1.setName("child-1");
stu1.setAge(11);
stuMapper.insert(stu1);
}
public void saveChild2() {
Stu stu2 = new Stu();
stu2.setName("child-2");
stu2.setAge(22);
stuMapper.insert(stu2);
}
运行数据库结果:空,没有插入任何数据。
③、外层开启默认事务,里层开启新建事务
外层事务出现异常不影响里面新建事务提交 ,分别提交
@Transactional(propagation = Propagation.REQUIRED)
@Override
public void testPropagationTrans() {
stuService.saveParent();
stuService.saveChildren();
int a = 1 / 0;
}
public void saveParent() {
Stu stu = new Stu();
stu.setName("parent");
stu.setAge(19);
stuMapper.insert(stu);
}
//开启新建事务
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveChildren() {
saveChild1();
//int a = 1 / 0; //异常点 java.lang.ArithmeticException: / by zero
saveChild2();
}
public void saveChild1() {
Stu stu1 = new Stu();
stu1.setName("child-1");
stu1.setAge(11);
stuMapper.insert(stu1);
}
public void saveChild2() {
Stu stu2 = new Stu();
stu2.setName("child-2");
stu2.setAge(22);
stuMapper.insert(stu2);
}
运行数据库结果:
id | name | age |
---|---|---|
28 | child-1 | 11 |
29 | child-2 | 22 |
6、传播方式 NOT_SUPPORTED 挂起,以非事务方式运行
①、外层非事务,里面挂起事务
异常之前都直接提交,异常之后都不执行
@Service
public class TestTransServiceImpl implements TestTransService {
@Autowired
private StuService stuService;
@Override
public void testPropagationTrans() {
stuService.saveParent();
stuService.saveChildren();
int a = 1 / 0;
}
}
@Service
public class StuServiceImpl implements StuService {
@Autowired
private StuMapper stuMapper;
public void saveParent() {//父方法直接调用,直接插入
Stu stu = new Stu();
stu.setName("parent");
stu.setAge(19);
stuMapper.insert(stu);
}
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void saveChildren() {//child1方法没有回滚
saveChild1();
int a = 1 / 0; //异常点 java.lang.ArithmeticException: / by zero
saveChild2(); //由于前面异常不在往后执行,也不回滚前面代码
}
public void saveChild1() {
Stu stu1 = new Stu();
stu1.setName("child-1");
stu1.setAge(11);
stuMapper.insert(stu1);
}
public void saveChild2() {
Stu stu2 = new Stu();
stu2.setName("child-2");
stu2.setAge(22);
stuMapper.insert(stu2);
}
}
运行数据库结果:
id | name | age |
---|---|---|
33 | parent | 19 |
34 | child-1 | 11 |
②、外层默认事务,里面挂起事务
外层事务传播,由于里层事务抛出异常回滚外层数据,由于里层挂起所以有提交一条
@Transactional(propagation = Propagation.REQUIRED)
@Override
public void testPropagationTrans() {
stuService.saveParent();
stuService.saveChildren();
int a = 1 / 0;
}
public void saveParent() {
Stu stu = new Stu();
stu.setName("parent");
stu.setAge(19);
stuMapper.insert(stu);
}
//挂起事务
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void saveChildren() {
saveChild1();
int a = 1 / 0; //异常点 java.lang.ArithmeticException: / by zero
saveChild2();
}
public void saveChild1() {
Stu stu1 = new Stu();
stu1.setName("child-1");
stu1.setAge(11);
stuMapper.insert(stu1);
}
public void saveChild2() {
Stu stu2 = new Stu();
stu2.setName("child-2");
stu2.setAge(22);
stuMapper.insert(stu2);
}
运行数据库结果:
id | name | age |
---|---|---|
36 | child-1 | 11 |
7、传播方式 NEVER如果有事务抛出异常,以非事务方式运行
①、外层不开启事务,子级开启NEVER事务
子级事务与外层保持一致,非事务方式运行。
//或者@Transactional(propagation = Propagation.SUPPORTS、Propagation.NOT_SUPPORTED)也一样结果
@Override
public void testPropagationTrans() {
stuService.saveParent();
stuService.saveChildren();
}
public void saveParent() {
Stu stu = new Stu();
stu.setName("parent");
stu.setAge(19);
stuMapper.insert(stu);
}
@Transactional(propagation = Propagation.NEVER)
public void saveChildren() {
saveChild1();
int a = 1 / 0; //异常点 java.lang.ArithmeticException: / by zero
saveChild2();
}
public void saveChild1() {
Stu stu1 = new Stu();
stu1.setName("child-1");
stu1.setAge(11);
stuMapper.insert(stu1);
}
public void saveChild2() {
Stu stu2 = new Stu();
stu2.setName("child-2");
stu2.setAge(22);
stuMapper.insert(stu2);
}
运行数据库结果:
id | name | age |
---|---|---|
47 | parent | 19 |
48 | child-1 | 11 |
②、外层开启默认事务,子级开启NEVER事务
强制不能有事务,不然抛出异常
org.springframework.transaction.IllegalTransactionStateException:
Existing transaction found for transaction marked with propagation 'never'
@Transactional(propagation = Propagation.REQUIRED) //或者Propagation.REQUIRES_NEW 一样结果
@Override
public void testPropagationTrans() {
stuService.saveParent();
stuService.saveChildren();
}
public void saveParent() {
Stu stu = new Stu();
stu.setName("parent");
stu.setAge(19);
stuMapper.insert(stu);
}
@Transactional(propagation = Propagation.NEVER)
public void saveChildren() {
saveChild1();
int a = 1 / 0; //异常点 java.lang.ArithmeticException: / by zero
saveChild2();
}
public void saveChild1() {
Stu stu1 = new Stu();
stu1.setName("child-1");
stu1.setAge(11);
stuMapper.insert(stu1);
}
public void saveChild2() {
Stu stu2 = new Stu();
stu2.setName("child-2");
stu2.setAge(22);
stuMapper.insert(stu2);
}
运行数据库结果: 空,没有插入任何数据。
8、传播方式 NESTED嵌套事务
①、外层开启默认事务,子级开启NESTED事务
属于嵌套事务,外层事务出现异常,子事务也将回滚
@Transactional(propagation = Propagation.REQUIRED)
@Override
public void testPropagationTrans() {
stuService.saveParent();
stuService.saveChildren();
int a = 1 / 0;
}
public void saveParent() {
Stu stu = new Stu();
stu.setName("parent");
stu.setAge(19);
stuMapper.insert(stu);
}
@Transactional(propagation = Propagation.NESTED)
public void saveChildren() {
saveChild1();
saveChild2();
}
public void saveChild1() {
Stu stu1 = new Stu();
stu1.setName("child-1");
stu1.setAge(11);
stuMapper.insert(stu1);
}
public void saveChild2() {
Stu stu2 = new Stu();
stu2.setName("child-2");
stu2.setAge(22);
stuMapper.insert(stu2);
}
运行数据库结果:空,没有插入任何数据。
②、外层开启默认事务,子级开启NESTED事务
子事务发生异常,如果不try catch,将影响外层事务;将全部回滚
@Transactional(propagation = Propagation.REQUIRED)
@Override
public void testPropagationTrans() {
stuService.saveParent();
stuService.saveChildren();
}
public void saveParent() {
Stu stu = new Stu();
stu.setName("parent");
stu.setAge(19);
stuMapper.insert(stu);
}
@Transactional(propagation = Propagation.NESTED)
public void saveChildren() {
saveChild1();
int a = 1 / 0; //异常点 java.lang.ArithmeticException: / by zero
saveChild2();
}
public void saveChild1() {
Stu stu1 = new Stu();
stu1.setName("child-1");
stu1.setAge(11);
stuMapper.insert(stu1);
}
public void saveChild2() {
Stu stu2 = new Stu();
stu2.setName("child-2");
stu2.setAge(22);
stuMapper.insert(stu2);
}
运行数据库结果:空,没有插入任何数据。
子事务发生异常,如果不ry catch,将不影响外层事务;将造常往下执行
@Transactional(propagation = Propagation.REQUIRED)
@Override
public void testPropagationTrans() {
stuService.saveParent();
try {
stuService.saveChildren();
}catch (Exception e){
e.printStackTrace();
}
System.out.println("继续往下执行中。。。。");
}
public void saveParent() {
Stu stu = new Stu();
stu.setName("parent");
stu.setAge(19);
stuMapper.insert(stu);
}
@Transactional(propagation = Propagation.NESTED)
public void saveChildren() {
saveChild1();
int a = 1 / 0; //异常点 java.lang.ArithmeticException: / by zero
saveChild2();
}
public void saveChild1() {
Stu stu1 = new Stu();
stu1.setName("child-1");
stu1.setAge(11);
stuMapper.insert(stu1);
}
public void saveChild2() {
Stu stu2 = new Stu();
stu2.setName("child-2");
stu2.setAge(22);
stuMapper.insert(stu2);
}
运行数据库结果:
java.lang.ArithmeticException: / by zero
at com.xiao.service.impl.StuServiceImpl.saveChildren(StuServiceImpl.java:65)
继续往下执行中。。。。
id | name | age |
---|---|---|
59 | parent | 19 |
③、父方法不开启事务,子方法开启事务
只有子级有事务回滚,其他以非事务方式运行。
@Override
public void testPropagationTrans() {
stuService.saveParent(); //以非事务方式运行
stuService.saveChildren(); //新的整体事务运行
}
public void saveParent() {
Stu stu = new Stu();
stu.setName("parent");
stu.setAge(19);
stuMapper.insert(stu);
}
//由于父级没有事务,所以这里创建一个新事务运行 像是REQUIRED
@Transactional(propagation = Propagation.NESTED)
public void saveChildren() {
saveChild1();
int a = 1 / 0; //异常点 java.lang.ArithmeticException: / by zero
saveChild2();
}
public void saveChild1() {
Stu stu1 = new Stu();
stu1.setName("child-1");
stu1.setAge(11);
stuMapper.insert(stu1);
}
public void saveChild2() {
Stu stu2 = new Stu();
stu2.setName("child-2");
stu2.setAge(22);
stuMapper.insert(stu2);
}
运行数据库结果:
id | name | age |
---|---|---|
61 | parent | 19 |
四、事务属性
1、事务特性 ACID
-
原子性 (Atomicity): 强调事务的不可分割,要么一起成功,要么一起失败
-
一致性 (Consistency) :事务的执行的前后数据的完整性保持一致.(总量保持不变)
-
隔离性 (Isolation) : 一个事务执行的过程中,不应该受到其他事务的干扰
-
持久性(Durability) :事务一旦结束,数据就持久到数据库
2、不同隔离级别引发的问题
-
脏读 :一个事务读到了另一个事务的未提交的数据
-
不可重复读 :一个事务中 两次读取的数据的内容不一致,另一个事务unpdate并提交导致的
-
虚幻读 :一个事务中 两次读取的数据的数量不一致,insert、delete的数据导致多次查询结果不一致.
3、设置事务隔离级别解决的问题
-
读写提交 :脏读,不可重复读,虚读都有可能发生
-
读已提交 :避免脏读。但是不可重复读和虚读有可能发生
-
可重复读 :避免脏读和不可重复读.但是虚读有可能发生。当使用可重复读隔离级别时,在事务执行期间会锁定该事务以任何方式引用的所有行。因此,如果在同一个事务中发出同一个SELECT语句两次或更多次,那么产生的结果数据集总是相同的。因此,使用可重复读隔离级别的事务可以多次检索同一行集,并对它们执行任意操作,直到提交或回滚操作终止该事务。
-
串行化 :避免以上所有读问题.。在事务A执行期间,禁止其它事务对这个表进行添加、更新、删除操作。
隔离级别 | 脏读 | 不可重复读 | 虚读 |
---|---|---|---|
读未提交 | × | × | × |
读已提交 | √ | × | × |
可重复读 | √ | √ | × |
串行化 | √ | √ | √ |
4、数据库默认的隔离级别
Mysql 默认:可重复读
Oracle 默认:读已提交
五、为何spring可以支持事务
①事务底层并不是spring支持的,而是有各个数据库支持的,数据库通过连接可以开启事务、提交事务、回滚事务。
②数据库中的事务提交和回滚是区分连接的。
③而spring管理者连接来控制事务开启和回滚,来控制事务;
④对于多个请求经过tomcat会从tomcat连接池中获取一个线程去请求controller层,所以存在线程安全问题,里面通过使用ThreadLock保证每个线程使用的连接是本线程的,而不会导致回滚或提交到其他请求的SQL。
3、事务定义信息
- 隔离级别
- 传播行为: PPROPAGATION_REQUIRED 支持当前事务,如果不存在 就新建一个(默认)
- 超时信息 //timeout=30,默认是30秒
- 是否只读 //readOnly