I'm using Spring Boot and Spring Data.
In the Service Layer, which is better, try to insert the record and catch the "Already Inserted" Exception by the unique key and than translate it into the business exception or use the repository to find the record and than throw the business exception directly?
Database PK is the best approach to maintain uniqueness constraint, if you try approach of querying and checking for PK then you could get in race condition where it will pass the unique check but fails in insert, so any way SQL exception thrown should be handled.
So it is better to handle via Exception and translate to meaning full business error.
Related
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?
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.
I am working on play framework using jpa, I have a field with an unique constraint, after "try" to persist an entity with a repeated value, the framework shows an error page like this:
error page
When I try to catch this exception...
try{
JPA.em().persist(nArtist);
}catch(Exception e){
form.reject("username","user already exist");
return badRequest(create_artist.render(form));
}
The page still shows the message... ( I tried already with rollback exception ).
Pdta: That JPA.em() is the only time I called the em.
The call to EntityManager.persist does not guarantee changes to be flushed to the database immediately (which is the point at which constraint violations would emerge). If you want to force a flush, call EntityManager.flush right after persist
Do not use exceptions to handle conditions that could normally occur in your application and, above all, do not use the generic java.lang.Exception. The exceptions thrown from the persistence layer at persist time could mean a lot more things than the specific constraint violation that you're after
I read that JPA caches SQL instructions to improve performance:
JPA providers like Hibernate can cache the SQL instructions they are
supposed to send to the database, often until you actually commit the
transaction. For example, you call em.persist(), Hibernate remembers
it has to make a database INSERT, but does not actually execute the
instruction until you commit the transaction.
I have a Java EE 6 application deployed to a Glassfish cluster with two instances. In the application there is a race condition where two Singletons do some expensive queries and then cache the results in a database table. They're doing the same work and trying to write the same record, so I sometimes get an exception:
java.sql.SQLIntegrityConstraintViolationException: ORA-00001: unique constraint (SOMESCHEMA.SOMETABLE_PK) violated
I decided the easiest way to deal with this would be to catch and ignore the exception:
// In a EJB with container-managed transactions.
public Entity getExpensiveEntity(int entityId) {
Entity entity = entityManager.find(Entity.class, entityId);
if (entity == null) {
try {
result = expensiveQueries();
entityManager.persist(result);
entityManager.flush();
} catch (SQLIntegrityConstraintViolationException ex) {
// The other instance already created the result, so get it.
result = jpa.find(result.getId());
}
}
return result;
}
I think the call to flush is necessary because otherwise the SQLIntegrityConstraintViolationException won't occur until the transaction ends somewhere up the EJB call stack, past catching and ignoring. Am I correct, is this a valid use case for flush? Is there a better way to handle this?
Reference
Correct use of flush() in JPA/Hibernate
A beginner’s guide to flush strategies in JPA and Hibernate
JPA and CMT -- Why Catching Persistence Exception is Not Enough?
JPA - create-if-not-exists entity? (see top-voted answer)
I am working on a spring MVC application. I have a DAO, Service and Controller. In Dao, I have a method which queries database to return a Sql rowset. I am checking sql rowset to be empty and if it is, I am throwing a Runtime exception. Also, according to the logic of the application, the query to database should return at least one row. So, basically I am assuming that if I get an empty sql rowset, then there is some issue, may be database is corrupt or something similar.
Is this the correct way to check for unknown exceptions. Or should I return the sql rowset as it is to the service? It may result in a null pointer exception when service uses this sql rowset.
The problem is if I throw exception in dao, I can't cover that part in the test cases. Means I have to put db in inconsistent state for this code to execute and test the exception handling part.
No, more generally you should never introduce a restriction into your application just because you don't have that situation or requirement right now. If you don't have any rows in a database your database might just be empty... Or you may be doing testing on that schema and it is currently empty. Your violating the concern of the dao by doing that.
Furthermore don't worry about the database, that is senseless. If you can't accept the tools you are working are functioning correctly then you won't be able to build anything at all. If you want to handle database exceptions have a controller which catches these exceptions and redirects to a view showing an error message:
#ControllerAdvice
public class ErrorHandler {
#ExceptionHandler(DataAccessException.class)
public String handleDatabaseException(DataAccessException ex) {
return "error";
}
#ExceptionHandler(CannotCreateTransactionException.class)
public String handleAccessException(CannotCreateTransactionException ex) {
return "database_error";
}
}
The DAO layer should not concern itself with business rules. It should simply abstract the data operations so that the service layer does not need to concern itself with how and where is the data being stored.
In my opinion, the DAO should simply return an empty result set. It will be up to the service to know what to do with an empty result set, since at the service layer is usually where the business logic is stored.
I have this Spring Data CrudRepository which handles the CRUD operations on a DB.
#Repository
public interface IUserRepository extends CrudRepository<User, String> {
}
User is the Entity of a User table of my DB. CrudRepository adds namely the following operations to the repository:
delete(String ID)
findOne(String ID)
save(User user)
As stated in the documentation, the delete and find operations throw IllegalArgumentException in case the given id is null while the save operation doesn't throw any exception.
The problem is that the javadoc of the CrudRepository makes no mention about the other exceptions thrown by these operations. For example it doesn't tell that the delete(String ID) operation throws a EmptyResultDataAccessException in case the provided ID is nonexistent in the DB.
In the javadoc of the save(User user) operation it's not clear which exceptions are thrown in case you insert a new User which breaks one data integrity constraint (on unique fields and foreign keys). Moreover it doesn't warn you whether you are writing a new or existent User: it just creates a new User or overwrites if existent (so it's a Insert + Update operation).
In a enterprise application I should be able to catch every throwable exception an operation can throw and I should read about that in the operation's javadoc.
Do you know any clear documentation about CrudRepository exceptions?
Spring has built-in exception translation mechanism, so that all exceptions thrown by the JPA persistence providers are converted into Spring's DataAccessException - for all beans annotated with #Repository (or configured).
There are four main groups -
NonTransientDataAccessException - these are the exceptions where a retry of the same operation would fail unless the cause of the Exception is corrected. So if you pass non existing id for example, it will fail unless the id exists in the database.
RecoverableDataAccessException - these are the "opposite" of the previous one - exceptions which are recoverable - after some recovery steps. More details in the API docs
ScriptException - SQL related exceptions, when trying to process not well-formed script for example.
TransientDataAccessException - these are the exception when recovery is possible without any explicit step, e.g. when there is a timeout to the database, you are retrying after few seconds.
That said, the ideal place to find documentation about all exceptions - is in the API itself - just go through the hierarchy of DataAccessException.
I capture the parent exception DataAccessException.
import org.springframework.dao.DataAccessException;