什么是事务?
数据库事务是指一系列严密操作,要么全部成功,要么全部失败。它有四种特性:原子性、一致性、隔离性和持久性。
而spring事务是封装在数据库事务之上的一种事务处理机制,它有两种管理方式:编程式事务和声明式事务。在平时使用中,我们大多使用@Transactional声明式事务来管理,这也是spring推荐的方式,下面例子也统一采用此种方式。
下面我们主要来看看spring事务的传播机制
spring事务的传播机制
spring事务的传播机制有七种:REQUIRED、REQUIRES_NEW、NESTED、SUPPORTS、NOT_SUPPORTED、MANDATORY和NEVER。
首先要知道事务的传播指的是一个方法调用另外一个方法并将事务传递给它,而传播机制是针对被调用者,控制它是否被传播或者被怎样传播。注:后面多次提到 此方法在/不在事务中,指的是调用者是否开启了事务。
下面我们来仔细分析一下这几种传播方式。
REQUIRED(有事务则加入,没有则创建)
REQUIRED是spring事务的默认方式,如果当前方法不在事务中,就创建一个新的事务;如果当前方法在事务中,就加入到这个事务中。
举个例子,我们操作book书籍表和title章节表,先插入book表,然后再插入title表(下面几种传播方式都以这两张表为例)。BookServiceImpl中方法(调用者)调用TitleServiceImpl中方法(被调用者)。下面分两种情况:
1、调用者有事务
@Slf4j
@Service
@AllArgsConstructor
public class BookServiceImpl implements BookService {
private final BookMapper bookMapper;
private final TitleService titleService;
@Override
@Transactional
public void bookTransaction() {
String transactionName = TransactionSynchronizationManager.getCurrentTransactionName();
log.info("调用者中事务:{}", transactionName);
bookMapper.insert(new Book().setAuthor("zpg"));
//传播事务
titleService.titleTransaction();
}
}
@Slf4j
@Service
@AllArgsConstructor
public class TitleServiceImpl implements TitleService {
private final TitleMapper titleMapper;
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void titleTransaction() {
String transactionName = TransactionSynchronizationManager.getCurrentTransactionName();
log.info("被调用者中事务:{}", transactionName);
titleMapper.insert(new Title().setName("第一章"));
}
}
// 输出,被调用者使用的是调用者的事务
调用者中事务:com.example.demo.transaction.service.impl.BookServiceImpl.requiredTest
被调用者中事务:com.example.demo.transaction.service.impl.BookServiceImpl.requiredTest
2、调用者无事务
//BookServiceImpl类
@Override
public void bookTransaction() {
String transactionName = TransactionSynchronizationManager.getCurrentTransactionName();
log.info("调用者中事务:{}", transactionName);
bookMapper.insert(new Book().setAuthor("zpg"));
//传播事务
titleService.titleTransaction();
}
//TitleServiceImpl类
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void titleTransaction() {
String transactionName = TransactionSynchronizationManager.getCurrentTransactionName();
log.info("被调用者中事务:{}", transactionName);
titleMapper.insert(new Title().setName("第一章"));
}
// 输出,被调用者创建新事务
调用者中事务:null
被调用者中事务:com.example.demo.transaction.service.impl.TitleServiceImpl.requiredTest
REQUIRES_NEW(新事务,有事务就创建新事务)
如果当前方法存在事务,则把当前事务挂起,(这个挂起怎么理解呢?就是将现存的事务放在一边,我不用这个事务),自己新建一个事务。调用者事务和被调用者事务两不相干。
//BookServiceImpl类
@Override
@Transactional
public void bookTransaction() {
String transactionName = TransactionSynchronizationManager.getCurrentTransactionName();
log.info("调用者中事务:{}", transactionName);
bookMapper.insert(new Book().setAuthor("zpg"));
//传播事务
titleService.titleTransaction();
}
//TitleServiceImpl类
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void titleTransaction() {
String transactionName = TransactionSynchronizationManager.getCurrentTransactionName();
log.info("被调用者中事务:{}", transactionName);
titleMapper.insert(new Title().setName("第一章"));
}
// 输出,被调用者和调用者是两个不同事务
调用者中事务:com.example.demo.transaction.service.impl.BookServiceImpl.requiredTest
被调用者中事务:com.example.demo.transaction.service.impl.TitleServiceImpl.requiredTest
NESTED(嵌套事务,有事务就创建子事务)
如果当前方法存在事务,则创建一个子事务,等父事务提交以后,子事务再提交;如果当前没有事务,则新建事务。(子事务异常,父事务不一定回滚;但父事务异常,则子事务肯定回滚)
1、子事务异常
//BookServiceImpl类
@Override
@Transactional
public void bookTransaction() {
String transactionName = TransactionSynchronizationManager.getCurrentTransactionName();
log.info("调用者中事务:{}", transactionName);
bookMapper.insert(new Book().setAuthor("zpg"));
try {
// 传播事务,捕获异常
titleService.titleTransaction();
} catch (Exception e) {
e.printStackTrace();
}
}
//TitleServiceImpl类
@Override
@Transactional(propagation = Propagation.NESTED)
public void titleTransaction() {
String transactionName = TransactionSynchronizationManager.getCurrentTransactionName();
log.info("被调用者中事务:{}", transactionName);
titleMapper.insert(new Title().setName("第一章"));
throw new RuntimeException("子事务异常");
}
// 输出,title表插入回滚了,但是book表插入未回滚
调用者中事务:com.example.demo.transaction.service.impl.BookServiceImpl.requiredTest
被调用者中事务:com.example.demo.transaction.service.impl.BookServiceImpl.requiredTest
java.lang.RuntimeException: 子事务异常
2、父事务异常
//BookServiceImpl类
@Override
@Transactional
public void bookTransaction() {
String transactionName = TransactionSynchronizationManager.getCurrentTransactionName();
log.info("调用者中事务:{}", transactionName);
bookMapper.insert(new Book().setAuthor("zpg"));
//传播事务
titleService.titleTransaction();
throw new RuntimeException("父事务异常");
}
//TitleServiceImpl类
@Override
@Transactional(propagation = Propagation.NESTED)
public void titleTransaction() {
String transactionName = TransactionSynchronizationManager.getCurrentTransactionName();
log.info("被调用者中事务:{}", transactionName);
titleMapper.insert(new Title().setName("第一章"));
}
// 输出,book的插入和title的插入都回滚了
调用者中事务:com.example.demo.transaction.service.impl.BookServiceImpl.requiredTest
被调用者中事务:com.example.demo.transaction.service.impl.BookServiceImpl.requiredTest
java.lang.RuntimeException: 父事务异常
SUPPORTS(支持事务,有没有都可以)
如果当前方法存在事务,则加入事务;如果当前方法不存在事务,则以非事务方式运行。这个传播行为和不写没多大区别,以后有这需求,可以不用写@Transactional。
代码:略
NOT_SUPPORTED(不支持事务,有事务也是以非事务方式执行)
这个传播行为就是,不管咋地都传不到我身上,我就是一直以非事务方式执行。
//BookServiceImpl类
@Override
@Transactional
public void bookTransaction() {
String transactionName = TransactionSynchronizationManager.getCurrentTransactionName();
log.info("调用者中事务:{}", transactionName);
bookMapper.insert(new Book().setAuthor("zpg"));
//传播事务
titleService.titleTransaction();
}
//TitleServiceImpl类
@Override
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void titleTransaction() {
String transactionName = TransactionSynchronizationManager.getCurrentTransactionName();
log.info("被调用者中事务:{}", transactionName);
titleMapper.insert(new Title().setName("第一章"));
throw new RuntimeException("异常");
}
// 输出,虽然被调用者中有事务,也抛出异常,但是数据不会回滚
调用者中事务:com.example.demo.transaction.service.impl.BookServiceImpl.requiredTest
被调用者中事务:com.example.demo.transaction.service.impl.TitleServiceImpl.requiredTest
java.lang.RuntimeException: 异常
MANDATORY(必须有事务,没有就抛异常)
这个传播行为就是不管咋地我都要在事务中,如果当前方法在事务中,就加入当前事务中;如果当前方法不在事务中,就抛异常。
1、当前方法存在事务
//BookServiceImpl类
@Override
@Transactional
public void bookTransaction() {
String transactionName = TransactionSynchronizationManager.getCurrentTransactionName();
log.info("调用者中事务:{}", transactionName);
bookMapper.insert(new Book().setAuthor("zpg"));
//传播事务
titleService.titleTransaction();
}
//TitleServiceImpl类
@Override
@Transactional(propagation = Propagation.MANDATORY)
public void titleTransaction() {
String transactionName = TransactionSynchronizationManager.getCurrentTransactionName();
log.info("被调用者中事务:{}", transactionName);
titleMapper.insert(new Title().setName("第一章"));
}
// 输出,被调用者加入到当前事务中
调用者中事务:com.example.demo.transaction.service.impl.BookServiceImpl.requiredTest
被调用者中事务:com.example.demo.transaction.service.impl.BookServiceImpl.requiredTest
2、当前方法不存在事务
//BookServiceImpl类
@Override
public void bookTransaction() {
String transactionName = TransactionSynchronizationManager.getCurrentTransactionName();
log.info("调用者中事务:{}", transactionName);
bookMapper.insert(new Book().setAuthor("zpg"));
//传播事务
titleService.titleTransaction();
}
//TitleServiceImpl类
@Override
@Transactional(propagation = Propagation.MANDATORY)
public void titleTransaction() {
String transactionName = TransactionSynchronizationManager.getCurrentTransactionName();
log.info("被调用者中事务:{}", transactionName);
titleMapper.insert(new Title().setName("第一章"));
}
// 输出,无事务则抛异常
调用者中事务:null
org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'
NEVER(不可能有事务,有事务就抛异常)
此传播方式就是不管咋地,谁调用我,你就不许有事务,否则我就抛异常。
1、调用者有事务
//BookServiceImpl类
@Override
@Transactional
public void bookTransaction() {
String transactionName = TransactionSynchronizationManager.getCurrentTransactionName();
log.info("调用者中事务:{}", transactionName);
bookMapper.insert(new Book().setAuthor("zpg"));
//传播事务
titleService.titleTransaction();
}
//TitleServiceImpl类
@Override
@Transactional(propagation = Propagation.NEVER)
public void titleTransaction() {
String transactionName = TransactionSynchronizationManager.getCurrentTransactionName();
log.info("被调用者中事务:{}", transactionName);
titleMapper.insert(new Title().setName("第一章"));
}
// 输出,调用者有事务,则被调用者就抛异常
调用者中事务:com.example.demo.transaction.service.impl.BookServiceImpl.requiredTest
org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never'
2、调用者无事务
//BookServiceImpl类
@Override
public void bookTransaction() {
String transactionName = TransactionSynchronizationManager.getCurrentTransactionName();
log.info("调用者中事务:{}", transactionName);
bookMapper.insert(new Book().setAuthor("zpg"));
//传播事务
titleService.titleTransaction();
}
//TitleServiceImpl类
@Override
@Transactional(propagation = Propagation.NEVER)
public void titleTransaction() {
String transactionName = TransactionSynchronizationManager.getCurrentTransactionName();
log.info("被调用者中事务:{}", transactionName);
titleMapper.insert(new Title().setName("第一章"));
}
// 输出,调用者无事务,被调用者虽然有事务,但是会以无事务方式执行
调用者中事务:null
被调用者中事务:com.example.demo.transaction.service.impl.TitleServiceImpl.requiredTest
总结
接下来我们总结一下各种传播方式下,调用者和被调用者是怎么操作事务的。注:A方法是调用者,B方法是被调用者。对于A方法来说,就两种情况:有事务和无事务,而对于B方法来说,有七种情况,下面看看每种情况下B方法是怎样操作事务的。
A方法调用B方法,B方法定义的事务类型 | A方法有事务时 | A方法无事务时 |
---|---|---|
REQUIRED:默认 | B和A事务合并成一个事务 | B新建一个事务 |
REQUIRES_NEW:必须新的 | B新建一个事务,和A事务无关,互不影响 | B新建一个事务 |
NESTED:嵌套 | B新建一个A的子事务,A异常影响B,B异常不影响A | B新建一个事务 |
SUPPORTS:支持 | B加入到A事务中 | B无事务 |
NOT_SUPPORTED:不支持 | 挂起A事务,B以无事务方式执行 | B无事务 |
MANDATORY:强制 | B加入到A事务中 | B抛异常 |
NEVER:绝不 | B抛异常 | B无事务 |