Spring 针对 Java Transaction API (JTA)、JDBC、Hibernate 和 Java Persistence API (JPA) 等事务 API,实现了一致的编程模型,而 Spring 的声明式事务功能更是提供了极其方便的事务配置方式,配合 Spring Boot 的自动配置,大多数 Spring Boot 项目只需要在方法上标记 @Transactional 注解,即可一键开启方法的事务性配置。
public int createUserWrong2(String name){ this.createUserPublic(new UserEntity(name)); return userRepository.findByName(name).size(); }
//标记了@Transactional的public方法 @Transactional public void createUserPublic(UserEntity entity){ userRepository.save(entity); if (entity.getName().contains("test")) throw new RuntimeException("invalid username!"); }
但是!以下自己调用自己的方法是可以生效的,但是不符合分层规范:
自身注入一个自身,然后调用自身的类,实际开发最好不要这样搞。。
以下才是标准的调用方式:
// Controller中 @GetMapping("right") public int right2(@RequestParam("name"){ try { userService.createUserPublic(new UserEntity(name)); } catch (Exception ex) { log.error("create user failed because {}", ex.getMessage()); } return
// Service中 @Transactional public void createUserPublic(UserEntity entity){ userRepository.save(entity); if (entity.getName().contains("test")) throw new RuntimeException("invalid username!"); }
我们再通过一张图来回顾下 this 自调用、通过 self 调用,以及在 Controller 中调用 UserService 三种实现的区别:
通过 this 自调用,没有机会走到 Spring 的代理类;后两种改进方案调用的是 Spring 注入的 UserService,通过代理调用才有机会对 createUserPublic 方法进行动态增强。
代码中手动处理了异常——不生效
只有异常传播出了标记了 @Transactional 注解的方法,事务才能回滚。在 Spring 的 TransactionAspectSupport 里有个 invokeWithinTransaction 方法,里面就是处理事务的逻辑。可以看到,只有捕获到异常才能进行后续事务处理,以下是spring源码:
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
手动处理了异常,并没有抛出,以下代码中声明式事务是不会生效的:
@Service @Slf4j public class UserService{ @Autowired private UserRepository userRepository;
@Transactional public int createUserWrong1(String name){ try { this.createUserPrivate(new UserEntity(name)); throw new RuntimeException("error"); } catch (Exception ex) { log.error("create user failed because {}", ex.getMessage()); } } }
打开 Spring 的 DefaultTransactionAttribute 类能看到如下代码块,可以发现相关证据,通过注释也能看到 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 @Override public boolean rollbackOn(Throwable ex){ return (ex instanceof RuntimeException || ex instanceof
以下代码中事务是不会生效的:
@Service @Slf4j public class UserService{ @Autowired private UserRepository userRepository;