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

时间:2018-05-03 08:43:31

标签: 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
}

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

2。在退出

之前刷新EntityManager

在我的服务方法结束时,在flush上明确调用EntityManager。这将强制写入数据库,因此触发异常。然而,它可能效率低下,因为我现在必须注意在请求期间不要无故刷新多次。我也最好不要忘记它,否则异常就会消失在空气中。

3。制作两个服务类

@Transactional方法放在一个单独的spring bean中,并在主服务中尝试捕获它们。这很奇怪,因为我必须小心处理我的代码的一部分到位A和另一部分到位。

4。处理控制器中的DataIntegrityViolationException

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

5。不要抓住DataIntegrityViolationException

我已经在网上看到了几个资源,特别是与Hibernate结合使用时,建议捕获此异常是错误的,并且应该在保存之前检查条件(即检查用户是否存在手动查询)。这在并发方案中不起作用,即使对于事务也是如此。是的,您将获得与交易的一致性,但是当"其他人排在第一位时,您仍然会得到DataIntegrityViolationException"。因此,这不是一个可接受的解决方案。

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

使用Spring TransactionTemplate代替@Transactional。这是唯一令人满意的解决方案。然而,这是相当多的" clunky"使用比#34;只是在方法上投掷@Transactional"甚至Spring文档似乎都在推动你使用@Transactional

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

3 个答案:

答案 0 :(得分:1)

投票给(3),例如:

@Service
public class UserExtService extend UserService{
}

@Service
public class UserService {
    @Autowired
    UserExtService userExtService;

    public int saveUser(User user) {
        try {
            return userExtService.save(user);
        } catch (DataIntegrityViolationException e) {
          throw DuplicateUsername(username);// GlobalExceptionHandler to response
        }
        return 0;
    }

    @Transactional(rollbackFor = Exception.class)
    public int save(User user) {
        //...
        return 0;
    }
}

答案 1 :(得分:0)

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

  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 joinPont)
  {
    try
    {
      return joinPont.proceed();
    }
    catch (MyException e)
    {
      // ...
    }
  }  
}
  1. 最后,用法。
@Service
@Transactional
public class SomeService
{
  // ...

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

答案 2 :(得分:0)

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

当控制器抛出异常时,您可以在此类中捕获该异常并将响应状态更改为合适的状态,或添加有关该异常的其他信息

此方法可帮助您保持干净的代码

您有很多示例:

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

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