Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Spring MVC, where to catch Database exceptions

enter image description here

I am following the structure suggested above at ( http://viralpatel.net/blogs/spring3-mvc-hibernate-maven-tutorial-eclipse-example/ ). I tried adding a duplicate entry, which resulted in the following exception:

SEVERE: Servlet.service() for servlet [appServlet] in context with path [/cct] threw exception [Request processing failed; nested exception is org.springframework.dao.DataIntegrityViolationException: Duplicate entry '[email protected]' for key 'PRIMARY'; SQL [n/a]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: Duplicate entry '[email protected]' for key 'PRIMARY'] with root cause
com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '[email protected]' for key 'PRIMARY'
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
    at com.mysql.jdbc.Util.handleNewInstance(Util.java:411)
    at << removed for readability>> org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
    at com.sun.proxy.$Proxy26.addUser(Unknown Source)
    at com.bilitutor.cct.control.HomeController.signup(HomeController.java:56)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    << removed for readability>> 

I have the following questions:

  1. Why is the exception being caught by the Controller ( userService.addUser(user) in com.bilitutor.cct.control.HomeController ) and not by the DAO ( sessionFactory.getCurrentSession().save(user); ) and then bubbled up to the Controller?

  2. I understand that I am getting a org.springframework.dao.DataIntegrityViolationException because I am using the @Repository annotation, which perhaps does exception translation (correct me if I am wrong). In that case, when I catch the exception, how do I find the error code for it?

  3. As a best practice, on which layer (DAO, Service or Controller) is the best spot to catch the exception?

Relevant classes :

COntroller :

package com.bilitutor.cct.control;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.web.bind.annotation.ModelAttribute;

import com.bilitutor.cct.bean.*;
import com.bilitutor.cct.service.*;

/**
 * Handles requests for the application home page.
 */
@Controller
public class HomeController {

    @Autowired
    UserService userService;

    @ModelAttribute("user")
    public User getUserObect() {
      return new User();
    }

    private static final Logger logger = LoggerFactory.getLogger(HomeController.class);

    /**
     * Landing page. Just return the login.jsp
     */
    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String home(Model model) {
        logger.info("home() called");
        return "login";
    }

    /**
     * Login. Either forward to the user's homepage or return back the error
     */
    @RequestMapping(value = "/login", method = RequestMethod.GET)
    public String login(Model model) {
        logger.info("login() called");
        return "login";
    }

    /**
     * New User signup. If user already exists, send back the error, or send an email and forward to the email validation page
     */
    @RequestMapping(value = "/signup", method = RequestMethod.POST)
    public String signup(@ModelAttribute("user")User user, BindingResult result) {
        logger.info("signup() : email="+user.getEmail()+" pass="+user.getPassword()+" accessCode="+user.getAccessCode());
        userService.addUser(user);
        return "login";
    }

}

Service:

package com.bilitutor.cct.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.bilitutor.cct.dao.UserDAO;
import com.bilitutor.cct.bean.User;

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDAO userDAO;

    @Transactional
    public void addUser(User user) {
        userDAO.addUser(user);
    }

    @Transactional
    public void removeUser(String email) {
        userDAO.removeUser(email);
    }

}

DAO:

package com.bilitutor.cct.dao;

import com.bilitutor.cct.bean.User;

import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

@Repository
public class UserDAOImpl implements UserDAO {

    @Autowired
    private SessionFactory sessionFactory;

    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    public void addUser(User user) {
        sessionFactory.getCurrentSession().save(user);
    }

    public void removeUser(String email) {
        User user = (User) sessionFactory.getCurrentSession().load(User.class, email);
        if (user!=null) {
            sessionFactory.getCurrentSession().delete(user);
        }

    }
}
like image 779
Amarsh Avatar asked Aug 07 '13 04:08

Amarsh


People also ask

How do you handle exceptions in spring MVC?

Spring MVC Framework provides following ways to help us achieving robust exception handling. Controller Based - We can define exception handler methods in our controller classes. All we need is to annotate these methods with @ExceptionHandler annotation. This annotation takes Exception class as argument.

Where should exceptions be caught?

You should catch the exception when you are in the method that knows what to do. For example, forget about how it actually works for the moment, let's say you are writing a library for opening and reading files. Here, the programmer knows what to do, so they catch the exception and handle it.

How does spring boot handle database exceptions?

Exception HandlerThe @ExceptionHandler is an annotation used to handle the specific exceptions and sending the custom responses to the client. Define a class that extends the RuntimeException class. You can define the @ExceptionHandler method to handle the exceptions as shown.


1 Answers

Exception is occuring at service layer. You can see this in trace

at com.sun.proxy.$Proxy26.addUser(Unknown Source)

@Transactional
public void addUser(User user) {
    userDAO.addUser(user);
}

As previous answer says your transaction boundary is at service layer, so exception occurs there.

I would recommend to catch/throw proper business exceptions(checked exceptions) from service methods. Service methods incorporate your business logic so , if anything goes wrong it should be properly communicated to outer world through the exceptions that service methods throw. For eg : WeakPasswordException, UserNameExistsException etc

Regarding org.springframework.dao.DataIntegrityViolationException try calling getCause() to see the wrapped exception

like image 171
Subin Sebastian Avatar answered Oct 26 '22 15:10

Subin Sebastian