我正在将Spring Boot 2与带有基础MariaDB的spring-boot-starter-data-jpa一起使用。
我有一个带有唯一键“用户名”的表。如果违反了此约束,我想捕获DataIntegrityViolationException
,但似乎Spring正在记录DataIntegrityViolationException
,并且在记录后不会重新抛出(我的最佳猜测)。而是抛出MySQLIntegrityConstraintViolationException
。
我想在DataIntegrityViolationException
中抓住UserService.createUser(..)
。
以下是一些代码段:
@Repository
@Transactional(propagation = Propagation.MANDATORY)
public class UserRepository {
@PersistenceContext
private EntityManager entityManager;
public void save(User user) {
entityManager.persist(user);
}
}
@Service
@Transactional(value = Transactional.TxType.REQUIRED)
public class UserService {
@Autowired
private UserRepository userRepository;
private void createUser(User user){
userRepository.save(user);
}
Stacktrace:
2018-09-22 14:20:33.163 WARN 10700 --- [nio-8080-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 1062, SQLState: 23000
2018-09-22 14:20:33.163 ERROR 10700 --- [nio-8080-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper : Duplicate entry 'kkflf' for key 'user_username_uindex'
2018-09-22 14:20:33.163 ERROR 10700 --- [nio-8080-exec-1] o.h.i.ExceptionMapperStandardImpl : HHH000346: Error during managed flush [org.hibernate.exception.ConstraintViolationException: could not execute statement]
2018-09-22 14:20:33.177 ERROR 10700 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [user_username_uindex]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement] with root cause
com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry 'kkflf' for key 'user_username_uindex'
...
答案 0 :(得分:0)
我解决了这个问题。
在事务提交之前,不会发生异常,这很有意义。
我能够在控制器类的事务范围之外捕获异常。
答案 1 :(得分:0)
正如 kkflf 所说,异常通常不会在 txn 提交之前抛出。如果您需要抛出它,您可以在该行之后立即调用 flush()。
然而,我已经学会了不依赖它作为任何事情的条件逻辑的艰难方法。例如,我想尝试删除,如果这不起作用,请执行其他操作。但是如果有任何进一步的数据库活动会涉及该对象(即使是通过引用),系统(spring/hibernate/whathaveyou)将不喜欢该状态。
正确的流程是在尝试操作之前自己在代码中检查条件。换句话说,不要将数据库用作 IF 语句 :-)
// Just say 'no' to this:
if (DB doesn't like it) {
// do this
} else {
// do that
}