问题描述
我有一个带有Hibernate的REST Spring引导应用程序.为简单起见,让我们假设这个工作流程:
I have a REST Spring boot app with Hibernate. For simplicity let's assume this workflow:
- 控制器处理传入的请求,调用Service方法
- 服务方法为
@Transactional
,执行一些业务逻辑并调用持久性方法 - 持久性方法由DAO对象处理,将内容保存到数据库中.
- Controller handles incoming requests, calls Service methods
- Service methods are
@Transactional
, do some business logic and call Persistence methods - Persistence methods are handled by DAO objects, saving stuff into database.
数据库对用户的 username
具有 unique
约束.我现在的工作方式是这样:
The database has a unique
constraint on username
of a User. The way I have it working now is this:
- 客户端向控制器发送请求
- 控制器调用服务
- 服务尝试通过DAO保存对象.如果发生
DataViolationException
,服务将返回自定义异常 - 控制器捕获自定义异常并发送回适当的响应
- Client sends request to Controller
- Controller calls Service
- Service attempts to save an object through DAO. If
DataViolationException
occurs, Service returns a custom Exception - Controller catches the custom Exception and sends back appropriate response
伪代码是这样的:
The pseudocode is this:
public class UserController {
@RequestMapping("/user")
public User createUser(...){
try{
return userService.createUser(...);
} catch (UserAlreadyExistsException e){
// Do some processing and return error message to client
}
}
}
public class UserService {
@Transactional
public User createUser(...){
(...)
try{
userDAO.save(newUserObject);
} catch(DataIntegrityViolationException e){
throw new UserAlreadyExistsException(username);
}
}
}
但是,这样,当尝试创建重复的用户时,我会收到一条错误消息.
However, this way I am getting an error when a duplicate user is attempted to be created.
javax.persistence.RollbackException: Transaction marked as rollbackOnly
解决此问题的一种方法似乎是让 DataIntegrityViolationException
从事务中冒泡"(而不是将其捕获到Service中).但这意味着Controller必须处理持久性异常,我不喜欢这样.
One way to fix this seems to be to let the DataIntegrityViolationException
"bubble" up from the transaction (and not catch it in Service). But that means that the Controller has to handle persistence exceptions and I don't like that.
我更喜欢服务是否抛出了可理解的"异常供Controller处理.该服务知道什么持久性异常以及何时发生,并能够将广泛的 DataIntegrityViolationException
转换"为有意义的异常.
I prefer if the service threw "understandable" exceptions for the Controller to handle. The service knows what persistence exceptions to expect and when and is able to "translate" the broad DataIntegrityViolationException
into a meaningful one.
是否可以通过这种方式处理异常?我不特别喜欢使用"2层服务层"来实现这一目标.
Is there a way to handle the exceptions this way? I don't particularly like the idea of having a "2-layered service layer" to achieve this.
我想抛出自定义异常的另一个原因是编译器必须捕获该异常.我想强制控制器处理所有可能发生的异常.
Another reason I want to throw my custom Exception is that it is required by the compiler to be caught. I want to enforce the controller to handle all possible exceptions that may occur.
推荐答案
您的存储库需要扩展JpaRepository,并且在执行此操作时也需要对其进行扩展.您可以从该存储库使用saveAndFlush方法.这意味着,您的代码将立即在数据库上执行,并且异常将在完成事务之前引发,并且您可以在Catch块中捕获它.我还添加了用于删除操作的示例.
Your repository need to extends JpaRepository, and when you do that. You can use saveAndFlush method from that repository. That mean, you code will be immediately executed on database and exception will be throw before finish transaction and you will be able to catch it in Catch block. I added also sample for deleting operation.
public class UserService {
@Transactional
public User createUser(...){
(...)
try{
userDAO.saveAndFlush(newUserObject);
} catch(DataIntegrityViolationException e){
throw new UserAlreadyExistsException(username);
}
}
@Transactional
public void deleteUser(...){
(...)
try{
userDAO.delete(deletingUserObject);
userDAO.flush();
} catch(DataIntegrityViolationException e){
throw new UserException(username);
}
}
}
这篇关于在@Transactional服务方法中捕获DataIntegrityViolationException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!