在使用Spring事务时,我们只需在某方法上使用@Transactional注解简单的标注一下,便可以实现很强大的事务控制功能,这其中的缘由,看小编娓娓道来;
首先需要明确:在spring事务的切面中,实际上是一个Around切面,在注解的业务方法前后都可以被调用,而实际上实现切面操作的是TransactionInterceptor类;
[本篇我们先从实例代码看该注解]
1)在代码中使用@Transactional 注解,不设置事务的其他属性,默认只对RuntimeException和Error进行回滚:
@Transactional
public void rollback() throws SQLException {
// update db
throw new SQLException("exception");
}
事实证明:上述的代码并不会回滚;
2)对于需要回滚的异常,需要在注解中设置rollbackFor属性,但这时,对于RuntimeException和Error的异常依旧会回滚:
@Transactional(rollbackFor = {SQLException.class})
public void rollback() throws SQLException {
// update db
throw new RuntimeException("exception");
}
在上述代码片段中,虽然指定了SqlException,但方法体中的RuntimeException依旧会造成事务回滚;
3)嵌套事务
@Transactional
public void rollback() {
// updateA
try{
selfProxy.innelTransaction()
}catch(RuntimeException e){
//do nothing
}
//updateC
}
@Transactional
public void innelTransaction() throws SQLException {
// updateB
throw new RuntimeException("exception");
}
在上述代码中,innerTransaction方法中的异常并未拦截,在被rollback方法拦截之前,AOP已经获取了异常,此时,当前事务的readOnly会被设置为true,所以即使在rollback方法中进行了拦截和处理,代码依旧会造成事务回滚;
产生以上现象的原因:
内部方法(innelTransaction)抛出异常,触发了回滚,这时当前事务被设置为 rollback-only,当外部方法所在的事务提交时,Spring抛出以下异常,同时会将当前事务回滚;
org.springframework.transaction.UnexpectedRollbackException:
Transaction rolled back because it has been marked as rollback-only
在上述代码中:
即使innelTransaction方法不加事务处理,依旧会有这个异常;
原因:在外部方法中,使用了一个事务,当内部方法抛出异常时,事务会被标记为需要回滚,但是最终要在外部方法的事务提交时,校验,spring的回滚标志,检测到回滚标记就会在回滚事务,并抛出UnexceptedRollbackException;
若要控制上述代码不做回滚:
1)我们需要在内部方法体中,将异常捕获,通过Boolean返回值的方式来完成;
2)使用ServiceB的代理类来完成对functionB 的调用