Reputation: 1491
I need to know how can I catch the EntityNotFoundException
with the getOne()
method.
I know you will suggest me to go with a method like findById()
or findOne()
which are retrieving the real entity rather than a proxy Object.
But in my case, I have two foreign keys to set before inserting the object. So to set those keys If I use a method starting with "find*" you can clearly see there will be redundant database calls to fetch those entities.
But according to my knowledge with the aid of this proxy object which was return from the getOne()
method I can perform this operation with a single insert query.
So now the problem is in a case of an invalid foreign key passed to the getOne()
method I need to send an error response to the end user by catching the EntityNotFoundException
that the inserted foreign key is not valid.
How can I achieve this?
Upvotes: 1
Views: 765
Reputation: 892
I know the question is pretty old today but in this case, you must catch JpaObjectRetrievalFailureException
instead of EntityNotFoundException
.
Upvotes: 0
Reputation: 90457
If the insert is failed because of the invalid foreign key , hibernate will throw org.hibernate.exception.ConstraintViolationException
which internally has the name of the DB constraint that causes this exception. You can then use it to determine which foreign key causes it.
Please note that Spring may wrap this exception with it own Exception object due to its persistence exception translation feature. That means after you catch the exception thrown by spring-data , you may have to traverse the causal chain of the exception to find out ConstraintViolationException
. I normally use Guava for doing it . Something likes:
try{
repository.saveAndFlush(employee);
} catch (Exception ex){
Set<String> violateConstraintNames= Throwables.getCausalChain(ex).stream()
.filter(e -> e instanceof ConstraintViolationException)
.map(e -> (ConstraintViolationException) e)
.map(ConstraintViolationException::getConstraintName)
.collect(toSet());
if(violateConstraintNames.contain("employee_department_fk")){
throw new RuntimeException("Department does not exist");
}else if (violateConstraintNames.contain("employee_manager_fk")){
throw new RuntimeException("Manager does not exist");
}else{
throw ex;
}
}
The code looks ugly to me when comparing to using findById()
to get and check if the referenced objects are valid or not. It also leaks the DB implementation details (i.e name of the foreign key) to the codes which is something that I would avoid. You are right that it will introduce additional select SQL, but I would consider it as premature optimisation as select by ID should be very fast thanks to the database index. Also in the non-trivial application, it is very common that you will sooner or later find that you have to get those referenced objects for checking if it pass some business rules or not.
Upvotes: 1