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:
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?
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?
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);
}
}
}
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.
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.
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With