在春季@Transactional方法期间处理异常

2022-09-01 04:43:44

我试图弄清楚如何最好地处理持久性(以及潜在的其他)异常与Spring的.对于这篇文章,我将以用户注册的简单示例为例,这可能是由于重复的用户名而导致的。@TransactionalDataIntegrityViolationException

我尝试过以下事情,它们对我来说并不真正令人满意:

1. 幼稚的方法:抓住异常

val entity = UserEntity(...)
try {
    repo.save(entity)
} catch (e: DataIntegrityViolationException) {
    // not included: some checks for which constraint failed
    throw DuplicateUsername(username) // to be handled by the controller
}

这在方法中不起作用,因为在提交事务之前不会发生持久性异常,这发生在spring事务包装器中的我的服务方法之外。@Transactional

2. 退出前冲洗EntityManager

显式调用 at end 的服务方法。这将强制写入数据库,从而触发异常。但是,它可能效率低下,因为我现在必须注意在请求期间不要无缘无故地多次刷新。我也最好永远不要忘记它,否则例外会消失在稀薄的空气中。flushEntityManager

3. 创建两个服务类

将这些方法放在单独的弹簧豆中,并在主服务中尝试捕获它们。这很奇怪,因为我必须注意在A的位置执行代码的一部分,在B的位置执行另一部分。@Transactional

4. 控制器中的手柄DataIntegrityViolationException

只。。。不。控制器在处理数据库中的异常时没有业务(色相色调)。

5. 不要捕捉DataIntegrityViolationException

我在网络上看到过一些资源,特别是与Hibernate结合使用,建议捕获此异常是错误的,应该在保存之前检查条件(即通过手动查询检查用户名是否存在)。这在并发方案中不起作用,即使对于事务也是如此。是的,您将获得与交易的一致性,但是当“其他人先来”时,您仍然可以获得。因此,这不是一个可以接受的解决办法。DataIntegrityViolationException

7. 不要使用声明式事务管理

使用 Spring 的 TransactionTemplate 而不是 .这是唯一有点令人满意的解决方案。但是,使用起来比“只是扔在方法上”要“笨拙”得多,甚至Spring文档似乎也会促使您使用。@Transactional@Transactional@Transactional

我想就如何最好地处理这种情况提供一些建议。除了我上次提出的解决方案之外,还有更好的替代方案吗?


答案 1

我在我的项目中使用以下方法。

  1. 自定义批注。
public @interface InterceptExceptions
{
}
  1. 豆子和方面在春天的背景下。
<beans ...>
  <bean id="exceptionInterceptor" class="com.example.ExceptionInterceptor"/>

  <aop:config>
    <aop:aspect ref="exceptionInterceptor">
      <aop:pointcut id="exception" expression="@annotation(com.example.InterceptExceptions)"/>
      <aop:around pointcut-ref="exception" method="catchExceptions"/>
    </aop:aspect>
  </aop:config>
</beans>
import org.aspectj.lang.ProceedingJoinPoint;

public class ExceptionInterceptor
{

  public Object catchExceptions(ProceedingJoinPoint joinPoint)
  {
    try
    {
      return joinPoint.proceed();
    }
    catch (MyException e)
    {
      // ...
    }
  }  
}
  1. 最后是用法。
@Service
@Transactional
public class SomeService
{
  // ...

  @InterceptExceptions
  public SomeResponse doSomething(...)
  {
    // ...
  }
}

答案 2

您可以使用带@ControllerAdvice或@RestControllerAdvice注释的类来处理异常

当控制器引发异常时,您可以在此类中捕获它,并将响应状态更改为合适的状态或添加异常的额外信息

此方法可帮助您维护干净的代码

您有很多例子:

https://www.javainuse.com/spring/boot-exception-handling

https://dzone.com/articles/best-practice-for-exception-handling-in-spring-boo


推荐