1.什么是事务
事务指的是程序的一系列连续操作,在执行过程中如果其中的某一项操作失败,则所有的操作所造成影响将全部被撤销,简单的说就是要么全部成功,要么全部失败。
数据库向用户提供保存当前程序状态的方法,叫事务提交(commit);当事务执行过程中,使数据库忽略当前的状态并回到前面保存的状态的方法叫事务回滚(rollback)
2.七种事务的传播机制
spring在TransactionDefinition接口中定义了七个事务传播行为:
- propagation-requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,则加入到这个事务中,这个是默认选项。
- propagation-supports:如果当前有事务,则支持当前事务,如果当前没有事务,就以非事务方法执行。
- propagation-mandatory:如果当前有事务,则使用当前的事务,如果没有当前事务,就抛出异常。
- propagation-required_new:无论当前有没有事务,都要新建事务,如果当前存在事务,把当前事务挂起。
- propagation-not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
- propagation-never:以非事务方式执行操作,如果当前事务存在则抛出异常。
- propagation-nested:如果当前存在事务,则作为子事务在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作
3.七种传播机制详解
PersonsServiceImpl类:
@Service
public class PersonsServiceImpl implements IPersonsService {
@Autowired
private PersonsMapper personsMapper;
@Override
public void savePersonMessage() {
savePersonMessage1();
savePersonMessage2();
}
@Override
public void savePersonMessage1() {
Persons person = new Persons();
person.setName("普通用户1");
person.setAge(19);
saveOnePersonMessage(person);
}
@Override
public void savePersonMessage2() {
Persons person = new Persons();
person.setName("普通用户2");
person.setAge(19);
saveOnePersonMessage(person);
}
@Override
public void saveVIPPersonMessage() {
Persons person = new Persons();
person.setName("VIP用户");
person.setAge(19);
saveOnePersonMessage(person);
}
@Override
public Persons getPersonMessage(int id) {
return personsMapper.selectByPrimaryKey(id);
}
@Override
public void saveOnePersonMessage(Persons person) {
personsMapper.insert(person);
}
}
TestTransactionServiceImpl类:
import com.nebula.service.IPersonsService;
import com.nebula.service.ITestTransactionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class TestTransactionServiceImpl implements ITestTransactionService{
@Autowired
private IPersonsService personsService;
@Override
public void saveAllPersonsMessage() {
personsService.saveVIPPersonMessage();
personsService.savePersonMessage();
}
}
TestTransactionServiceTest单元测试类
import com.nebula.Application;
import com.nebula.service.IPersonsService;
import com.nebula.service.ITestTransactionService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class TestTransactionServiceTest {
@Autowired
private ITestTransactionService testTransactionService;
@Test
public void saveAllPersonsMessageTest(){
testTransactionService.saveAllPersonsMessage();
}
}
后面主要进行修改的是
PersonsServiceImpl
中的savePersonMessage
方法和TestTransactionServiceImpl
中的saveAllPersonsMessage
方法,并且在后面进行测试时会将原先的数据库表进行清空。
执行TestTransactionServiceTest
中saveAllPersonsMessageTest
方法进行测试
执行结果:结果正常录入数据库中
(1)propagation-requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,则加入到这个事务中,这个是默认选项。
/**
* Support a current transaction, create a new one if none exists.
* Analogous to EJB transaction attribute of the same name.
* <p>This is the default setting of a transaction annotation.
*/
REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),
测试一:
TestTransactionServiceImpl
中的saveAllPersonsMessage
不做处理;
PersonsServiceImpl
中的savePersonMessage
中加入报错int a = 1 / 0;
执行结果: 程序报错,数据库插入VIP用户和普通用户1,普通用户2因为报错没有插入进去。
@Override
public void saveAllPersonsMessage() {
saveVIPPersonMessage();
savePersonMessage();
}
@Override
public void savePersonMessage() {
savePersonMessage1();
int a = 1 / 0;
savePersonMessage2();
}
执行结果:
测试二:
TestTransactionServiceImpl
中的saveAllPersonsMessage
不做处理;
PersonsServiceImpl
中的savePersonMessage
中加入REQUIRED
事务;
执行结果: VIP用户插入,两个普通用户都没有插入
原因: savePersonMessage
中新建事务,由于其中一步执行出错,整个方法回滚;而saveAllPersonsMessage
中的saveVIPPersonMessage
方法不在事务中,不会回滚,数据依旧插入;
@Override
public void saveAllPersonsMessage() {
saveVIPPersonMessage();
savePersonMessage();
}
@Transactional(propagation = Propagation.REQUIRED)
@Override
public void savePersonMessage() {
savePersonMessage1();
int a = 1 / 0;
savePersonMessage2();
}
执行结果:
测试三:
TestTransactionServiceImpl
中的saveAllPersonsMessage
加入REQUIRED
事务;
PersonsServiceImpl
中的savePersonMessage
中不加入事务;
执行结果: 由于报错,整个事务回滚,所以所有的数据都没有插入进去;
原因: saveAllPersonsMessage
整个在一个事务中,所以一个出错全部回滚;
@Transactional(propagation = Propagation.REQUIRED)
@Override
public void saveAllPersonsMessage() {
saveVIPPersonMessage();
savePersonMessage();
}
@Override
public void savePersonMessage() {
savePersonMessage1();
int a = 1 / 0;
savePersonMessage2();
}
执行结果:
测试四:
TestTransactionServiceImpl
中的saveAllPersonsMessage
加入REQUIRED
事务;
PersonsServiceImpl
中的savePersonMessage
中加入REQUIRED
事务;
执行结果: 由于报错,整个事务回滚,所以所有的数据都没有插入进去;
原因: savePersonMessage
由于调用他的方法有事务,所以不用开启新的事务,直接使用上一个事务,所以saveAllPersonsMessage
整个在一个事务中,所以一个出错全部回滚;
@Transactional(propagation = Propagation.REQUIRED)
@Override
public void saveAllPersonsMessage() {
saveVIPPersonMessage();
savePersonMessage();
}
@Transactional(propagation = Propagation.REQUIRED)
@Override
public void savePersonMessage() {
savePersonMessage1();
int a = 1 / 0;
savePersonMessage2();
}
执行结果:
(2)propagation-supports:如果当前有事务,则支持当前事务,如果当前没有事务,就以非事务方法执行。
/**
* Support a current transaction, execute non-transactionally if none exists.
* Analogous to EJB transaction attribute of the same name.
* <p>Note: For transaction managers with transaction synchronization,
* {@code SUPPORTS} is slightly different from no transaction at all,
* as it defines a transaction scope that synchronization will apply for.
* As a consequence, the same resources (JDBC Connection, Hibernate Session, etc)
* will be shared for the entire specified scope. Note that this depends on
* the actual synchronization configuration of the transaction manager.
* @see org.springframework.transaction.support.AbstractPlatformTransactionManager#setTransactionSynchronization
*/
SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),
测试一:
TestTransactionServiceImpl
中的saveAllPersonsMessage
加入REQUIRED
事务;
PersonsServiceImpl
中的savePersonMessage
中加入加入SUPPORTS
事务;
执行结果: 程序报错,没有数据插入。
原因: saveAllPersonsMessage
中有事务,savePersonMessage
加入事务,所以报错全部回滚。
@Transactional(propagation = Propagation.REQUIRED)
@Override
public void saveAllPersonsMessage() {
saveVIPPersonMessage();
savePersonMessage();
}
@Transactional(propagation = Propagation.SUPPORTS)
@Override
public void savePersonMessage() {
savePersonMessage1();
int a = 1 / 0;
savePersonMessage2();
}
执行结果:
测试二:
TestTransactionServiceImpl
中的saveAllPersonsMessage
不加入事务;
PersonsServiceImpl
中的savePersonMessage
中加入SUPPORTS
事务;
执行结果: 程序报错,数据库插入VIP用户和普通用户1,普通用户2因为报错没有插入进去。
原因: saveAllPersonsMessage
中没有事务,savePersonMessage
没有可以加入的事务,所以以非事务形式执行,不会回滚。
@Override
public void saveAllPersonsMessage() {
saveVIPPersonMessage();
savePersonMessage();
}
@Transactional(propagation = Propagation.SUPPORTS)
@Override
public void savePersonMessage() {
savePersonMessage1();
int a = 1 / 0;
savePersonMessage2();
}
执行结果:
(3)propagation-mandatory:如果当前有事务,则使用当前的事务,如果没有当前事务,就抛出异常。
/**
* Support a current transaction, throw an exception if none exists.
* Analogous to EJB transaction attribute of the same name.
*/
MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),
测试一:
TestTransactionServiceImpl
中的saveAllPersonsMessage
加入REQUIRED
事务;
PersonsServiceImpl
中的savePersonMessage
中加入MANDATORY
事务;
执行结果: 程序报错,没有数据插入。
原因:saveAllPersonsMessage
中有事务,savePersonMessage
加入事务,所以报错全部回滚。
@Transactional(propagation = Propagation.REQUIRED)
@Override
public void saveAllPersonsMessage() {
saveVIPPersonMessage();
savePersonMessage();
}
@Transactional(propagation = Propagation.MANDATORY)
@Override
public void savePersonMessage() {
savePersonMessage1();
int a = 1 / 0;
savePersonMessage2();
}
执行结果:
测试二:
TestTransactionServiceImpl
中的saveAllPersonsMessage
不加入事务;
PersonsServiceImpl
中的savePersonMessage
中加入加入MANDATORY
事务;
执行结果: 抛出异常:org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'
,数据库中插入VIP用户数据
原因: saveAllPersonsMessage
中没有事务,savePersonMessage
中因为没有事务可用导致报错,数据不会插入,但saveVIPPersonMessage
中没有错误可以执行,所以VIP用户数据插入。
@Override
public void saveAllPersonsMessage() {
saveVIPPersonMessage();
savePersonMessage();
}
@Transactional(propagation = Propagation.MANDATORY)
@Override
public void savePersonMessage() {
savePersonMessage1();
int a = 1 / 0;
savePersonMessage2();
}
执行结果:
org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'
at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:364)
at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:475)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:289)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
(4)propagation-requierd_new:如果当前有事务,则挂起该事务,并且自己创建一个新的事务给自己使用;
/**
* Create a new transaction, and suspend the current transaction if one exists.
* Analogous to the EJB transaction attribute of the same name.
* <p><b>NOTE:</b> Actual transaction suspension will not work out-of-the-box
* on all transaction managers. This in particular applies to
* {@link org.springframework.transaction.jta.JtaTransactionManager},
* which requires the {@code javax.transaction.TransactionManager} to be
* made available to it (which is server-specific in standard Java EE).
* @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager
*/
REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),
测试一:
TestTransactionServiceImpl
中的saveAllPersonsMessage
没有事务;
PersonsServiceImpl
中的savePersonMessage
中加入REQUIRES_NEW
事务;
执行结果: 程序报错,数据库中插入VIP用户数据。
原因: saveAllPersonsMessage
中没有事务,savePersonMessage
会新建事务,所以普通用户部分报错全部回滚。
// @Transactional(propagation = Propagation.REQUIRED)
@Override
public void saveAllPersonsMessage() {
personsService.saveVIPPersonMessage();
personsService.savePersonMessage();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Override
public void savePersonMessage() {
savePersonMessage1();
int a = 1 / 0;
savePersonMessage2();
}
执行结果:
测试二:
TestTransactionServiceImpl
中的saveAllPersonsMessage
加入REQUIRED
事务;
PersonsServiceImpl
中的savePersonMessage
中加入REQUIRES_NEW
事务;
PersonsServiceImpl
中的saveVIPPersonMessage
中加入REQUIRES_NEW
事务;
执行结果: 程序报错,数据库中插入VIP用户数据。
原因: saveAllPersonsMessage
中有事务,但是由于在savePersonMessage
和saveVIPPersonMessage
中用的都是REQUIRES_NEW
所以实际上都是新开的事务,与saveAllPersonsMessage
中的事务并不是同一个,所以在savePersonMessage
中报错回滚,普通用户没有插入进去,saveAllPersonsMessage
方法也进行回滚,但是因为事务并不是同一个,所以saveVIPPersonMessage
中的插入数据并没有回滚,数据插入进去。
@Transactional(propagation = Propagation.REQUIRED)
@Override
public void saveAllPersonsMessage() {
personsService.saveVIPPersonMessage();
personsService.savePersonMessage();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Override
public void savePersonMessage() {
savePersonMessage1();
int a = 1 / 0;
savePersonMessage2();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Override
public void saveVIPPersonMessage() {
Persons person = new Persons();
person.setName("VIP用户");
person.setAge(19);
saveOnePersonMessage(person);
}
执行结果:
测试三:
TestTransactionServiceImpl
中的saveAllPersonsMessage
加入REQUIRED
事务;
PersonsServiceImpl
中的savePersonMessage
中不加入事务;
PersonsServiceImpl
中的saveVIPPersonMessage
中加入REQUIRES_NEW
事务;
执行结果: 程序报错,数据库中插入VIP用户数据。
原因: saveAllPersonsMessage
中有事务,savePersonMessage
中报错导致回滚,但是由于saveVIPPersonMessage
中和saveAllPersonsMessage
不是一个事务,所以VIP数据插入成功。
@Transactional(propagation = Propagation.REQUIRED)
@Override
public void saveAllPersonsMessage() {
personsService.saveVIPPersonMessage();
personsService.savePersonMessage();
}
// @Transactional(propagation = Propagation.REQUIRES_NEW)
@Override
public void savePersonMessage() {
savePersonMessage1();
int a = 1 / 0;
savePersonMessage2();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Override
public void saveVIPPersonMessage() {
Persons person = new Persons();
person.setName("VIP用户");
person.setAge(19);
saveOnePersonMessage(person);
}
执行结果:
(5)propagation-not_supported:如果当前有事务,则把事务挂起,自己不用事务去运行数据库操作;
/**
* Execute non-transactionally, suspend the current transaction if one exists.
* Analogous to EJB transaction attribute of the same name.
* <p><b>NOTE:</b> Actual transaction suspension will not work out-of-the-box
* on all transaction managers. This in particular applies to
* {@link org.springframework.transaction.jta.JtaTransactionManager},
* which requires the {@code javax.transaction.TransactionManager} to be
* made available to it (which is server-specific in standard Java EE).
* @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager
*/
NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),
测试一:
TestTransactionServiceImpl
中的saveAllPersonsMessage
加入REQUIRED
事务;
PersonsServiceImpl
中的savePersonMessage
中加入NOT_SUPPORTED
事务;
执行结果: 程序报错,数据库中插入普通用户1。
原因: saveAllPersonsMessage
中有事务,savePersonMessage
中报错,父事务回滚,所以VIP用户没有插入,但是由于savePersonMessage
不支持事务,所以里面报错后,普通用户2没有插入,普通用户1插入也不会回滚
@Transactional(propagation = Propagation.REQUIRED)
@Override
public void saveAllPersonsMessage() {
personsService.saveVIPPersonMessage();
personsService.savePersonMessage();
}
@Transactional(propagation = Propagation.NOT_SUPPORTED)
@Override
public void savePersonMessage() {
savePersonMessage1();
int a = 1 / 0;
savePersonMessage2();
}
执行结果:
(6)propagation-never:如果当前有事务存在,则抛出异常;
/**
* Execute non-transactionally, throw an exception if a transaction exists.
* Analogous to EJB transaction attribute of the same name.
*/
NEVER(TransactionDefinition.PROPAGATION_NEVER),
测试一:
TestTransactionServiceImpl
中的saveAllPersonsMessage
加入REQUIRED
事务;
PersonsServiceImpl
中的savePersonMessage
中加入NEVER
事务;
执行结果: 程序报错Existing transaction found for transaction marked with propagation 'never'
,数据库没有数据。
原因: saveAllPersonsMessage
中有事务,savePersonMessage
中不支持事务,直接报错,事务回滚,没有插入数据
@Transactional(propagation = Propagation.REQUIRED)
@Override
public void saveAllPersonsMessage() {
personsService.saveVIPPersonMessage();
personsService.savePersonMessage();
}
@Transactional(propagation = Propagation.NEVER)
@Override
public void savePersonMessage() {
savePersonMessage1();
int a = 1 / 0;
savePersonMessage2();
}
执行结果:
org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never'
at org.springframework.transaction.support.AbstractPlatformTransactionManager.handleExistingTransaction(AbstractPlatformTransactionManager.java:406)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:354)
at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:475)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:289)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688)
at com.nebula.service.impl.PersonsServiceImpl$$EnhancerBySpringCGLIB$$c608f071.savePersonMessage(<generated>)
at com.nebula.service.impl.TestTransactionServiceImpl.saveAllPersonsMessage(TestTransactionServiceImpl.java:19)
(7)propagation-nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与required类似的操作。
这个和requires_new需要区分一下,主要区别在于requires_new是一个新事务,但是nested是一个嵌套的子事务,下面是引用网上大牛对这个的区分,后面进行表格和代码测试区分:
前六个策略类似于EJB CMT,第七个(PROPAGATION_NESTED)是Spring所提供的一个特殊变量。它要求事务管理器或者使用JDBC 3.0,Savepoint API提供嵌套事务行为(如Spring的DataSourceTransactionManager)
REQUIRES_NEW 启动一个新的, 不依赖于环境的 “内部” 事务. 这个事务将被完全 commited 或 rolled back而不依赖于外部事务, 它拥有自己的隔离范围, 自己的锁, 等等. 当内部事务开始执行时, 外部事务将被挂起, 内务事务结束时,外部事务将继续执行.
另一方面, NESTED 开始一个 “嵌套的” 事务, 它是已经存在事务的一个真正的子事务. 潜套事务开始执行时,它将取得一个 savepoint. 如果这个嵌套事务失败, 我们将回滚到此 savepoint. 潜套事务是外部事务的一部分,只有外部事务结束后它才会被提交.
/**
* Execute within a nested transaction if a current transaction exists,
* behave like {@code REQUIRED} otherwise. There is no analogous feature in EJB.
* <p>Note: Actual creation of a nested transaction will only work on specific
* transaction managers. Out of the box, this only applies to the JDBC
* DataSourceTransactionManager. Some JTA providers might support nested
* transactions as well.
* @see org.springframework.jdbc.datasource.DataSourceTransactionManager
*/
NESTED(TransactionDefinition.PROPAGATION_NESTED);
通过表格进行对比saveAllPersonsMessage
作为方法A,savePersonMessage
作为方法B,A调用B,表象相同,即代码执行结果相同,但是执行原理不同,可以对比表象不同的场景。
异常状态 | REQUIRES_NEW(两个独立事务) | NESTED(B的事务嵌套在A的事务中) | 表象 |
---|---|---|---|
A抛异常 B正常 | A回滚,B正常提交 | A与B一起回滚 | 不同 |
A正常 B抛异常 | 1.如果A中捕获B的异常(如trycache),并没有继续向上抛异常,则B先回滚,A再正常提交; 2.如果A未捕获B的异常,默认则会将B的异常向上抛,则B先回滚,A再回滚 | 1.如果A中捕获B的异常,并没有继续向上抛异常,则AB一起提交,B报错回滚; 2.如果A未捕获B的异常,默认则会将B的异常向上抛,则A与B一起回滚 | 相同 |
A抛异常 B抛异常 | B先回滚,A再回滚 | A与B一起回滚 | 相同 |
A正常 B正常 | B先提交,A再提交 | A与B一起提交 | 相同 |
测试场景:
TestTransactionServiceImpl
中的saveAllPersonsMessage
加入REQUIRED
事务;
PersonsServiceImpl
中的savePersonMessage
分别加入NESTED
事务和REQUIRES_NEW
事务;
执行结果:
NESTED
事务:报错,没有数据插入
REQUIRES_NEW
事务:报错,但是savePersonMessage
中数据插入成功
原因:
NESTED
事务:由于是子事务,主事务报错会一起回滚
REQUIRES_NEW
事务:是新事务,与父方法的事务不同,不受父方法事务控制,单独执行,所以数据插入成功
@Transactional(propagation = Propagation.REQUIRED)
@Override
public void saveAllPersonsMessage() {
personsService.saveVIPPersonMessage();
personsService.savePersonMessage();
int a = 1 / 0;
}
@Transactional(propagation = Propagation.NESTED)
@Override
public void savePersonMessage() {
savePersonMessage1();
savePersonMessage2();
}
@Transactional(propagation = Propagation.REQUIRED)
@Override
public void saveAllPersonsMessage() {
personsService.saveVIPPersonMessage();
personsService.savePersonMessage();
int a = 1 / 0;
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Override
public void savePersonMessage() {
savePersonMessage1();
savePersonMessage2();
}
执行结果:
NESTED
事务:
REQUIRES_NEW
事务: