首页 > 解决方案 > 在 Spring 中的 @Transactional 方法期间处理异常

问题描述

我试图弄清楚如何结合 Spring 的@Transactional. 对于这篇文章,我将举一个简单的用户注册示例,这可能是DataIntegrityViolationException由于用户名重复而导致的。

我尝试过以下事情,但对我来说并不是很满意:

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
}

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

EntityManager2.退出前冲洗

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

3.制作两个服务类

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

4.DataIntegrityViolationException控制器中的手柄

就是不行。控制器在处理来自数据库的异常时没有任何业务(hue hue hue)。

5.不要抓DataIntegrityViolationException

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

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

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

我想要一些关于如何最好地处理这种情况的建议。我上次提出的解决方案有更好的替代方案吗?

标签: javaspringhibernatetransactions

解决方案


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

  1. 自定义注释。
public @interface InterceptExceptions
{
}
  1. 春季上下文中的 Bean 和方面。
<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(...)
  {
    // ...
  }
}

推荐阅读