我有一个非常基本的创建用户控制器。
@PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<String> createUser(@RequestBody UserInput userInput) {
userControllerService.createUser(userInput);
return ResponseEntity.ok("success");
}
UserControllerService#createUser方法是一个包含多个SimpleJpaRepository#保存调用的事务。
@Transactional
@Override
public void createUser(UserInput userInput) {
userRepository.save(userInput);
profileRepository.save(userInput.getProfile());
}
我希望能够让数据库处理唯一的约束违规,并能够通知客户端有关特定的违规。
例如,如果我想在且仅在我得到特定约束冲突时通知客户端。
@PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<String> createUser(@RequestBody UserInput userInput) {
try {
userControllerService.createUser(userInput);
} catch (DuplicateUserNameException e) {
return new ResponseEntity<>("", HttpStatus.BAD_REQUEST);
} catch (DuplicateEmailException e) {
return new ResponseEntity<>("", HttpStatus.BAD_REQUEST);
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
return ResponseEntity.ok("success");
}
但是,任何约束冲突都会在UserControllerService#createUser的末尾引发DataIntegrityViolationException。而且DataIntegrityViolationException太脆弱,无法依赖。它的原因只有SQLState:23505(违反唯一约束)和非结构化消息,例如:
错误:重复的键值违反了唯一约束条件“unique_user”详细信息:密钥(电子邮件)=(john@test.com)已存在。
即使我添加了自定义异常处理,它也永远不会运行,因为DataIntegrityViolationException直到第一次实际调用数据库的方法结束时才遇到。例如,这没有任何效果。
@Transactional
@Override
public void createUser(UserInput userInput) {
try
userRepository.save(userInput);
} catch (Exception e) {
throw new DuplicateUserNameException();
}
try {
profileRepository.save(userInput.getProfile());
} catch (Exception e) {
throw new DuplicateEmailException();
}
}
我是不是走错了路?看起来这是非常基本的功能,应该是可能的。
我能想到的最好方法是添加一些代码来解析来自DataIntegrityViolationException的消息,但这有其局限性,例如,对同一个表的两次插入对应用程序有不同的意义。一次插入可能直接来自用户,第二次插入可能是应用程序生成的内容。仅通过解析详细消息可能无法在事务结束时区分这两者。
我是否应该考虑其他实现?
2条答案
按热度按时间ubbxdtey1#
如果我没理解错的话,您似乎希望有一种可靠的方法来确定
DataIntegrityViolationException
何时被抛出,导致它的确切原因是什么,例如是否是由于特定用例的重复电子邮件或重复用户名或其他原因。最简单的方法是不依赖于抛出的异常来确定,而是在将数据保存到DB之前主动发出一些SQL来验证它,例如:
j1dl9f462#
你提到的问题
即使我添加了自定义异常处理,它也永远不会运行,因为DataIntegrityViolationException直到第一次实际调用数据库的方法结束时才遇到。例如,这没有任何效果。
应该可以通过通知Hibernate立即执行事务来解决,方法是调用
这至少会导致在该行上引发异常。