JPA Exception Handling in Spring WebApp - java

I am working on a Spring web application having DAO, Service, Controller, View layers. JPA is being used for database access. The approach that I am using for JPA exception handling is as below.
public class DBException extends RuntimeException {
implemented constructors to call appropriate RuntimeException constructors.
}
public MyDAOClass {
public void save(Object object) {
try {
entityManager.persist(object);
}catch(PersistenceException e) {
throw new DBException("Error occurred in save", e);
}
}
}
public class MyServiceClass {
public void serviceMethod() {
dao.save(object);
}
}
public class MyRestController {
public void controllerMethod() {
service.save();
}
#ExceptionHandler(DBException.class)
public String handleDBException(DBException e, HttpServletRequest req) {
implemented code to log & handle exception.
}
}
I wanted to have more information in the exception, that's the reason to create custom DBException and also I don't wanted to propagate PersistenceException to other layers.
Is this correct approach for exception handling ?
Should I log the exception in all layers or logging it at the end at controller is good ?
I am catching PersistenceException and rethrowing DBException. Since DBException is a RuntimeException I think spring transaction should rollback in event of exception ?
Thanks.

Is this correct approach for exception handling ?
Yes, you should never propagate exceptions that are specific to layer to the layer above it. If you are expecting the clients of the data layer to recuperate from the exception then create a new custom checked exception and throw it from the catch block in save method.
Should I log the exception in all layers or logging it at the end at
controller is good ?
Logging should be done only at one place, since logging at multiple levels can confuse the programmer examining the stack trace about the original source of exception. In this case I think its best to log the SQLException in the catch block of save method, since you are rethrowing a RuntimeException, the caller of save method may not necessarily catch the exception and log it.
I am catching PersistenceException and rethrowing DBException. Since
DBException is a RuntimeException I think spring transaction should
rollback in event of exception ?
Yes, Spring transactions rollback in case of RuntimeException. This is because the caller code cannot do much in case of RuntimeException, since it represents unrecoverable condition. This behaviour is defined in the Docs.
More about best practices for exception handling here

Related

How to preserve the original stack trace when exception is wrapped in another custom exception and rethrown

I have created Spring boot application with the following layers
Controller
BusinessLayer
DAOImplLayer
Controller calls the Business layer and Business layer calls the DAOImplLayer. I have created two different custom exceptions (BusinessException and DAOException)
In DAOImpl class I'm catching DataAccessException and throwing DAOException (with dataAccessException object in parameter since SonarQube is complaining to Either log or rethrow DataAccessException exception.)
catch (DataAccessException dataAccessException)
{
throw new DAOException(dataAccessException, INTERNAL_SERVER_ERROR, CODE_INTERNAL_SERVER,
dataAccessException.getCause().getMessage());
}
In BusinessImpl I'm catching DAOException and throwing BusinessException
catch (SecurityDAOException e)
{
some logic
throw new BusinessException(e, e.type, e.code, UNABLE_TO_PROCESS_REQUEST);
}
I've written common Handler class to log the exceptions.
#ExceptionHandler(BusinessException.class)
protected ResponseEntity<Object> handleEntityNotFound(BusinessException businessException)
{
if (null != businessException.getCause())
{
LOG.error("BusinessException: ", businessException);
}
some logic
}
How to preserve the original stack trace. I am looking of something similar
Exception in thread "main" java.lang.ArithmeticException: / by zero
at com.exception.copy.Layer3.getLayer3(Layer3.java:8)
at com.exception.copy.Layer2.getLayer2(Layer2.java:9)
at com.exception.copy.Layer1.main(Layer1.java:9)
Is it fine to have a custom exception for each layer?
Nearly every Exception allows you to pass a 'cause' Exception in the constructor so that when you throw an exception in a catch-block, you can append the original exception like this.
Usually when the exception is logged or printed, the output contains the original exception appended with words like "caused by:". Also when you catch such a wrapped exception, you can call Throwable.getCause() to retrieve the exception that caused the current exception.
Of course when you forget to add the cause in the constructor, the original exception and the real cause is lost.
Wrapping exceptions is a common practice in cases where the original cause will not tell the user what went wrong, because the message and exception type might be to low-level.

Does the rollback transactions feature only work when an exception is thrown?

I have this declaration above my class declaration. Will spring only rollback when DAOException is thrown?
#Transactional(rollbackFor = { ManagerException.class, DAOException.class })
If that is the case, how can I rollback a bad commit without throwing an exception. Currently my database package functions return an error message, I want to roll back and still display the error message to the user without throwing an exception and crashing the app.
The documentation says:
You can also indicate a required rollback programmatically. Although very simple, this process is quite invasive, and tightly couples your code to the Spring Framework's transaction infrastructure:
public void resolvePosition() {
try {
// some business logic...
} catch (NoProductInStockException ex) {
// trigger rollback programmatically
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}

Three-tier architecture and exceptions

It's considered good practice to have an exception for each layer of application (i.e. PresentationException, ServiceException, PersistenceException etc). But what if my service-layer directly calls DAO methods (methods of persistence layer) without additional operations.
Like this:
public class MyService {
private IPersonDAO dao = new PersonDAO();
public void deletePerson(int id) {
dao.deletePerson(id);
}
}
Should I wrap this DAO method invocation with a try-catch block and rethrow possible exceptions as ServiceException? Should each DAO method throw only PersistenceException?
Well your Dao exceptions are irrelevant to service layer and service layer has nothing to do with dao layer exceptions. Right approach would be to catch dao exception and rethrow new custom exception to service layer.
If you need to debug the exceptions and you want exact cause, you can use getCause() and getSuppressed() methods.
Should I wrap this DAO method invocation with try-catch block and rethrow possible exception as ServiceException? Should each DAO method throw only PersistenceException?
---> Yes wrap it. You can throw other exceptions from dao layer. See example below :
public class MyDao {
public Entity getMyEntity(int id) throws ObjectNotFoundException, PersistenceException {
try {
// code to get Entity
// if entity not found then
throw new ObjectNotFoundException("Entity with id : " + id + "Not found.");
} catch(Exception e) { // you can catch the generic exception like HibernateException for hibernate
throw new PersistenceException("error message", e);
}
}
}
Yes. Its recommended, as you mentioned, to have exceptions for layers; as they can tell what's the problem from the service perspective instead of an DB.
Yes, you should wrap those exceptions in any case, as your service layer clients would otherwise be forced to also deal with the database layer. This would make things overly complicated. Note that the bit of work required to be done in the service layer is meaningless small when compared to the work that would be required to deal with database exceptions in the layer above the service layer.

Better Exception Handling in JPA

I used EJB3/JPA when persisting my entities and I am happy on how it is able to manage my DB related
task.
My only concern is on the exception handling. My sample code when saving entity always comes in this flavor.
Most of the tutorials that I read on the net comes in this flavor also with no regards to exception handling.
#Stateless
public class StudentFacade{
#PersistenceContext(unitName = "MyDBPU")
private EntityManager em;
public void save(Student student) {
em.persist(student);
}
}
But I dont know whats the best way of exception handling in an EJB app?
What should be the best way when handling exception?
Is this how others is handling the exception? A try catch block on your session facade?
#Stateless
public class StudentFacade{
#PersistenceContext(unitName = "MyDBPU")
private EntityManager em;
public void save(Student student) {
try {
em.persist(student);
} catch(Exception e) {
//log it or do something
}
}
}
or letting the method throw an exception?
public void save(Student student) throws Exception {
em.persist(student);
}
I dont know if my understanding is correct since I am still learning EJB.
Thanks
The idea of exception handling is doing some logic at a single point in case of any failure.
The try catch will be used at the final point where you need to handle exception or you need to convert an exception to another exception
Say your app has many layers namely Action, Facade, Persist
Delegate exception
In this case any exception that is thrown on Facade can be thrown to the above action layer.
In action the particular exception will be caught and handled with proper error message.
//This is in Facade Layer
public void save(Student student) throws AppException{
//exceptions delegated to action layer
//call to Persist Layer
}
Converting General Exception to App exception
Say in persistence you get and DBException like sqlException. This exception should not be send as such to Action or Facade layer, so we catch the particular exception and then throw a new exception (a user defined exception for application)
//This is in Persist Layer
public void save(Student student) throws AppException{
//converting general exception to AppException and delegating to Facade Layer
try{
em.persist(student);//call to DB. This is in Persist Layer
}catch(Exception e){
throw new AppException("DB exception", e)
}
}
In action Layer
You will catch your exception in action and then handle exception there
//This is in Action layer
public void callSave(Student student){
try{
//call Facade layer
}catch(AppException e){
//Log error and handle
}
}
If you want you method to throw the exception got from em.persistance(...), then don't surround that statement with that try/catch block (cause that will catch every exception that's in that block).
The way you approach this problem depends on the application, whether there already exists some legacy code or not. In the case there is legacy code, I suggest you use the same approach (even in for some cases it's not speed optimal) to maintain consistency.
Otherwise I'd suggest following exceptions' "rule of thumb" - they should be treated in the first place where you have all the information you need about them to take an action, else throw them so someone else can handle. (If you throw them away make sure to throw the most specific form of exception that you could throw (not the general Exception)). Handling exceptions when using JPA is no different then handling Java exceptions in general.
I hope this was simple enough information about exceptions without starting a "religious conversation".
If your combination is ejb with jpa, then all jpa exceptions are runtime exceptions.
ejb handling 2 types of exceptions 1) Application Exception 2) System Exception
Application Exceptions checked exceptions basically we are using business validation and business rules.
System Exceptions are runtime exceptions, so that if any runtime excpetion happend ejb container will interfer and convert the runtime exception as remote exception.
For ex:
in dao layer
public void store(Cargo cargo) {
entityManager.persist(cargo);
}
All jpa exceptions are runtime exceptions only.
in ejb service layer:
public TrackingId bookNewCargo(UnLocode originUnLocode,
UnLocode destinationUnLocode,
Date arrivalDeadline) {
Cargo cargo = new Cargo(trackingId, routeSpecification);
cargoRepository.store(cargo);
return cargo.getTrackingId();
}
in the ejb layer if any runtime exception happend, ejb container will interfere and convert into remote exception.
In the interface layer:
public String register() {
try {
String trackingId = bookingServiceFacade.bookNewCargo(
originUnlocode,
destinationUnlocode,
arrivalDeadline);
} catch (Exception e) {
throw new RuntimeException("Error parsing date", e);
}
so that like this jpa --> ejb --> interface

How to hide RuntimeException details from EJB client?

I have a JEE5 application that exposes services using (local) session beans.
When an internal fault occurs during service execution, a RuntimeException is thrown and encapsulated by JBoss(5.0.1) in a javax.ejb.EJBTransactionRolledbackException.
The problem is that client applications receiving this EJBTransactionRolledbackException can access detailled informations about the cause runtime exception, exposing internal architecture of my application. And I don't want that.
Instead, I would like JBoss to always encapsulate RuntimeException thrown by exposed session beans into a single (and simple) TechnicalException (with no cause).
What's the best way to achieve this ? (Using interceptors ? Using JBoss configuration ?)
Finally, based on previous answer and my personal researches, I retained the folowing solution.
I've created an interceptor dedicated to manage server faults :
public class FaultBarrierInterceptor {
#AroundInvoke
public Object intercept(final InvocationContext invocationContext) throws Exception {
try {
return invocationContext.proceed();
} catch (final RuntimeException e) {
final Logger logger = Logger.getLogger(invocationContext.getMethod().getDeclaringClass());
logger.error("A fault occured during service invocation:" +
"\n-METHOD: " + invocationContext.getMethod() +
"\n-PARAMS: " + Arrays.toString(invocationContext.getParameters()), e);
throw new TechnicalException();
}
}}
The thrown technical exception extends EJBException and does not expose the cause RuntimeException:
public class TechnicalException extends EJBException {}
I use this interceptor in all public services :
#Stateless
#Interceptors({FaultBarrierInterceptor.class})
public class ShoppingCardServicesBean implements ShoppingCardServices { ...
This is an implementation of the Fault Barrier pattern.
Any runtime exception is catched, logged and a fault is signaled to the client (without internal details) using a TechnicalException. Checked exceptions are ignored.
RuntimeException handling is centralized and separated from any business methods.
Any RuntimeException extends java.lang.Exception.
The EJB spec provides handling for 2 types of exceptions (Application and System)
If you'd like to throw a System Exception, you would usually do it like so:
try {
.... your code ...
}catch(YourApplicationException ae) {
throw ae;
}catch(Exception e) {
throw new EJBException(e); //here's where you need to change.
}
To hide the internal details of your system exception, simply replace:
throw new EJBException(e);
with:
throw new EJBException(new TechnicalException("Technical Fault"));
Hope this is what you were looking for.
Cheers

Categories

Resources