在之前的文章中,我们讲过如果有两个事务,A和B,均是Required new的传递类型,而我们在A中调用B。如果B中抛出了异常(RuntimeException,Spring的事务机制只会补货RumtimeException),而A中将异常进行了catch,则A会抛出以下的错误
Exception in thread "main" org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
从而强制将事务进行回滚,Spring采用该机制是为了防止用户错误的将引发回滚的异常进行捕获而忘记继续抛出。
以下的例子展示了这种情况:
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void testTransaction() {
LOGGER.info("start test transaction");
CustApplyTicketService service = ApplicationContextHolder.getBean("transactionService",
TransactionService.class);
Person person = new Person();
person.setName("测试");
service.saveEntitySelective(ticket);
try {
service.testInnerTransaction();
} catch (Exception e) {
LOGGER.error("", e);
}
LOGGER.info("finish test transaction");
}
@Override
@Transactional
public void testInnerTransaction() {
throw new RuntimeException("this is a inner exception");
}
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("conf/applicationContext-test.xml");
TransactionService service = ac.getBean("transactionService", TransactionService.class);
service.testTransaction();
}
那有的时候,我们有这种需求,我们就是希望A回滚,但是又不想通过这种方式抛出异常来进行。那有没有不抛出异常的回滚方式呢?
我们可以通过设置TransactionStatus的rollBackOnly标志位的方式来实现。
比如如下的方式:
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void testTransaction() {
LOGGER.info("start test transaction");
CustApplyTicketService service = ApplicationContextHolder.getBean("transactionService",
TransactionService.class);
Person person = new Person();
person.setName("测试");
service.saveEntitySelective(ticket);
try {
service.testInnerTransaction();
} catch (Exception e) {
LOGGER.error("", e);
// 这部是关键
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
LOGGER.info("finish test transaction");
}
@Override
@Transactional
public void testInnerTransaction() {
throw new RuntimeException("this is a inner exception");
}
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("conf/applicationContext-test.xml");
TransactionService service = ac.getBean("transactionService", TransactionService.class);
service.testTransaction();
}
在上面,我们并不会收到UnexpectedRollbackException的异常。而由于我们人工的设置了回滚标志,所以数据库仍会进行回滚。