问题
I am using Spring Boot 2 with spring-boot-starter-data-jpa with an underlying MariaDB.
I have table with a unique key "username". I want to catch DataIntegrityViolationException
if this constraint is violated, but it seems like Spring is logging DataIntegrityViolationException
and does not rethrow the after logging(my best guess). MySQLIntegrityConstraintViolationException
is thrown instead.
I would like to catch DataIntegrityViolationException
in UserService.createUser(..)
.
Here are a couple of code snippets:
@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'
...
回答1:
I solved the problem.
The exception does not occur until the transaction commits, which makes perfect sense.
I was able to catch the exception outside the transaction scope in a controller class.
回答2:
As kkflf said, the exception won't normally throw until the txn commits. If you need it to throw, you can call flush() right after the line.
However, I have learned the hard way not to rely on that as conditional logic for anything. For example, I wanted to attempt a delete and if that didn't work, do something else. But if there's any further database activity that would involve that object (even by reference), the system (spring/hibernate/whathaveyou) will not like the state is in.
The correct flow is to check the condition yourself in code before you attempt the operation. In other words, don't use the database as an IF statement :-)
// Just say 'no' to this:
if (DB doesn't like it) {
// do this
} else {
// do that
}
来源:https://stackoverflow.com/questions/52456783/cannot-catch-dataintegrityviolationexception