I have the following controller in an MVC application with Spring and Hibernate:
#RequestMapping(value = { "/mypage"}, method = RequestMethod.POST)
public ModelAndView showPage(Model model, HttpServletRequest request) {
ModelAndView mv = new ModelAndView();
try {
// Here I call a Service and then a DAO, where I can get HibernateException
} catch (Exception ex) {
ex.printStackTrace();
}
mv.setViewName("mypage");
return mv;
I'm not sure the exceptions are handled correctly here.
First of all:
I don't throw any checked exception (HibernateException is also unchecked).
However I need to catch all the exceptions, because I want to show to the user the same page anyway with a notification error (instead of an error page).
In my DAO I didn't catch or rethrew any exceptions.
My questions are:
Is it fine to catch and deal with exceptions in that way?
Should I also catch HibernateException separately in the controller?
Should I do something more than just logging?
Should I also catch the exception in the DAO and then rethrow it? Should rethrow a different type of exception to the controller?
HibernateException encapsulates the actual root cause than can provide you enough information for you to generate meaningful user-friendly messages. Read the Exception Handling section of their documentation.
If the Session throws an exception, including any SQLException,
immediately rollback the database transaction, call Session.close()
and discard the Session instance. Certain methods of Session will not
leave the session in a consistent state. No exception thrown by
Hibernate can be treated as recoverable.
This means that other than catching the exception to transform it or wrap it into another kind of exception, catching the exception won't help you: using the session after won't work as expected anyway.
refer this
Related
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.
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
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();
}
}
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
My Spring MVC app is full of methods that look like this:
#RequestMapping(value = "/foo", method = RequestMethod.GET)
public final void foo(HttpServletRequest request, ModelMap modelMap){
try{
this.fooService.foo();
}
catch (Exception e){
log.warn(e.getMessage(), e);
}
}
Exceptions are caught and logged but not handled otherwise.
The fooService called above does the same thing, never throwing exceptions up to the controller but catching and logging them. So, actually this controller exception code will never get invoked.
What's the best and simplest approach to implement proper exception handling in my app?
Get rid of all catch statements if all they do is logging carelessly. catch is meant to handle the error, not hide it.
Once all these catches are removed, install one global exception resolver in Spring MVC (1, 2, 3, ...) Simply implement this trivial interface:
public interface HandlerExceptionResolver {
ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex);
}
In your exception resolver you might simply log the exception once and let it go as unprocessed (return null), so that error mappings in web.xml will forward request to proper error page. Or you can handle exception yourself and render some error page. AFAIK in simplest case there is no need for register exception resolver, just define it as a Spring bean/annotate with #Service.
Remember, catch the exception only when you know what to do with. Logging is only for troubleshooting, it doesn't handle anything.
BTW this:
log.warn(e.getMessage(), e);
is not only a very poor exception handling, but it is also slightly incorrect. If your exception does not have a message, you will see mysterious null just before the stack trace. If it does, the message will appear twice (tested with Logback):
22:51:23.985 WARN [main][Foo] OMG! - this is the exception message
java.lang.IllegalStateException: OMG! - this is the exception message
at Foo.bar(Foo.java:20) ~[test-classes/:na]
...sometimes undesirable, especially when exception message is very long.
UPDATE: When writing your own exception logger consider implementing both org.springframework.web.servlet.HandlerExceptionResolver and org.springframework.core.Ordered. The getOrder() should return something small (like 0) so that your handler takes precedence over built-in handlers.
It just happened to me that org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver running prior to my handler returned HTTP 500 without logging the exception.