在Spring的@Transactional方法期间处理异常

die*_*n07 8 java spring hibernate transactions

我试图找出如何最好地结合使用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
}
Run Code Online (Sandbox Code Playgroud)

@Transactional方法中这是行不通的,因为持久性异常只有在提交事务后才会发生,这在spring事务包装器中我的服务方法之外发生。

2. EntityManager退出前冲洗

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

3.进行两个服务类别

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

4.装DataIntegrityViolationException上控制器

就是不行。控制器在处理数据库中的异常方面没有任何业务(色相)。

5.不要抓 DataIntegrityViolationException

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

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

使用Spring TransactionTemplate代替@Transactional。这是唯一令人满意的解决方案。但是,使用它比“仅仅抛出@Transactional方法”要笨拙得多,甚至Spring文档似乎也会使您偏向使用@Transactional

我想要一些有关如何最好地处理这种情况的建议。我最后提出的解决方案还有更好的选择吗?

Ste*_*rnK 3

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

  1. 自定义注释。
public @interface InterceptExceptions
{
}
Run Code Online (Sandbox Code Playgroud)
  1. Spring 上下文中的 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>
Run Code Online (Sandbox Code Playgroud)
import org.aspectj.lang.ProceedingJoinPoint;

public class ExceptionInterceptor
{

  public Object catchExceptions(ProceedingJoinPoint joinPoint)
  {
    try
    {
      return joinPoint.proceed();
    }
    catch (MyException e)
    {
      // ...
    }
  }  
}
Run Code Online (Sandbox Code Playgroud)
  1. 最后,使用情况。
@Service
@Transactional
public class SomeService
{
  // ...

  @InterceptExceptions
  public SomeResponse doSomething(...)
  {
    // ...
  }
}
Run Code Online (Sandbox Code Playgroud)