我正在尝试与Spring的@Transactional
一起找出如何最好地处理持久性(以及可能的其他)异常。
对于这篇文章,我将采用用户注册的简单示例,由于用户名重复,可能会导致DataIntegrityViolationException
。
以下我尝试过的事情并不令我满意:
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方法之外。
EntityManager
在我的服务方法结束时,在flush
上明确调用EntityManager
。这将强制写入数据库,因此触发异常。然而,它可能效率低下,因为我现在必须注意在请求期间不要无故刷新多次。我也最好不要忘记它,否则异常就会消失在空气中。
将@Transactional
方法放在一个单独的spring bean中,并在主服务中尝试捕获它们。这很奇怪,因为我必须小心处理我的代码的一部分到位A和另一部分到位。
DataIntegrityViolationException
只是......不。控制器在处理数据库中的异常时没有业务(色调色调)。
DataIntegrityViolationException
我已经在网上看到了几个资源,特别是与Hibernate结合使用时,建议捕获此异常是错误的,并且应该在保存之前检查条件(即检查用户是否存在手动查询)。这在并发方案中不起作用,即使对于事务也是如此。是的,您将获得与交易的一致性,但是当"其他人排在第一位时,您仍然会得到DataIntegrityViolationException
"。因此,这不是一个可接受的解决方案。
使用Spring TransactionTemplate
代替@Transactional
。这是唯一令人满意的解决方案。然而,这是相当多的" clunky"使用比#34;只是在方法上投掷@Transactional
"甚至Spring文档似乎都在推动你使用@Transactional
。
我想就如何最好地处理这种情况提出一些建议。有没有比我上一次提出的解决方案更好的替代方案?
答案 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)
我在项目中使用以下方法。
public @interface InterceptExceptions
{
}
<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)
{
// ...
}
}
}
@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