Spring的事务
@Transactional
生效原则1:除非特殊配置(比如使用AspectJ静态织入实现AOP),否则只有定义在public方法上的@Transactional才能生效。
原因:Spring默认通过动态代理的方式实现AOP,对目标方法进行增强,private方法无法代理到,Spring自然也无法动态增强事务处理逻辑。
生效原则2:必须通过代理过的类从外部调用目标方法才能生效
回滚
事务即使生效不一定能回滚:通过AOP实现事务处理,使用try...catch...来包裹标记了@Transactional注解的方法,当方法出现了异常并且满足一定条件的时候,在catch里面我们可以设置事务回滚,没有异常则直接提交事务。
一定条件
第一,只有异常传播出了标记了@Transactional注解的方法,事务才能回滚。 //在Spring的TransactionAspectSupport类中 invokeWithinTransaction方法 try { // This is an around advice: Invoke the next interceptor in the chain. // This will normally result in a target object being invoked. retVal = invocation.proceedWithInvocation(); } catch (Throwable ex) { // target invocation exception completeTransactionAfterThrowing(txInfo, ex); throw ex; } finally { cleanupTransactionInfo(txInfo); }
第二,默认情况下,出现RuntimeException(非受检异常)或Error的时候,Spring才会回滚事务。即受检异常(一般是业务异常,或者说类似另一种方法的返回值,出现这样的异常可能业务还能完成,所以不会主动回滚);而Error或RuntimeException代表了非预期的结果,应该回滚 /** * The default behavior is as with EJB: rollback on unchecked exception * ({@link RuntimeException}), assuming an unexpected outcome outside of any * business rules. Additionally, we also attempt to rollback on {@link Error} which * is clearly an unexpected outcome as well. By contrast, a checked exception is * considered a business exception and therefore a regular expected outcome of the * transactional business method, i.e. a kind of alternative return value which * still allows for regular completion of resource operations. * <p>This is largely consistent with TransactionTemplate's default behavior, * except that TransactionTemplate also rolls back on undeclared checked exceptions * (a corner case). For declarative transactions, we expect checked exceptions to be * intentionally declared as business exceptions, leading to a commit by default. * @see org.springframework.transaction.support.TransactionTemplate#execute */ @Override public boolean rollbackOn(Throwable ex) { return (ex instanceof RuntimeException || ex instanceof Error); }
手动修复
1. 手动设置让当前事务处于回滚状态 @Transactional public void createUser(User user) { try { userDao.save(new User(user)); throw new RuntimeException("error"); } catch (Exception ex) { log.error("create user failed", ex); TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); } } 2. 在注释中声明,期望遇到所有的Exception都回滚事务(覆盖默认不回滚受检异常) @Transactional(rollbackFor = Exception.class) public void createUser(User user) { throws IOException { userDao.save(new User(user)); otherTask(); }
独立事务
如果方法涉及多个数据库操作,并希望将他们作为独立的事务进行提交或回滚,我们考虑进一步细化配置事务传播方式,也就是@Transactional 注解的 Propagation 属性。