在Spring MVC中,捕获数据库异常的位置

时间:2013-08-07 04:10:21

标签: hibernate spring-mvc exception-handling

enter image description here

我遵循上面建议的结构(http://viralpatel.net/blogs/spring3-mvc-hibernate-maven-tutorial-eclipse-example/)。我尝试添加重复条目,这导致以下异常:

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 'a@b.com' for key 'PRIMARY'; SQL [n/a]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: Duplicate entry 'a@b.com' for key 'PRIMARY'] with root cause
com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry 'a@b.com' 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>> 

我有以下问题:

  1. 为什么控制器(userService.addUser(user)中的com.bilitutor.cct.control.HomeController)而不是DAO(sessionFactory.getCurrentSession().save(user);)捕获异常,然后冒泡到控制器?

  2. 我知道我得到的是org.springframework.dao.DataIntegrityViolationException,因为我正在使用@Repository注释,这可能会进行异常翻译(如果我错了,请纠正我)。在这种情况下,当我捕获异常时,如何找到它的错误代码?

  3. 作为最佳做法,哪一层(DAO,服务或控制器)是捕获异常的最佳位置?

  4. 相关课程:

    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";
        }
    
    }
    

    服务:

    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);
            }
    
        }
    }
    

3 个答案:

答案 0 :(得分:1)

服务层发生异常。你可以在追踪中看到这个

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

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

如前所述,您的事务边界位于服务层,因此会发生异常。

我建议从服务方法中捕获/抛出正确的业务异常(已检查的异常)。服务方法包含您的业务逻辑,因此,如果出现任何问题,应通过服务方法抛出的异常将其正确地传达给外部世界。例如:WeakPasswordException,UserNameExistsException等

关于org.springframework.dao.DataIntegrityViolationException试试 getCause()查看已包装的异常

答案 1 :(得分:0)

1

我怀疑你把你的控制器处理程序方法作为事务边界,例如:

@RequestMapping
@Transactional
public String myHandler(..) {
  ...
}

只有在将更新同步到数据库后才能检测到重复行,如果您使用hibernate / jpa,这通常会在事务关闭时发生,因此您会看到它来自您的控制器

回想一下,@Transactional通过jdk代理/方面编织工作。

3

答案取决于您的业务需求。当发生特定异常时,您需要做什么?

答案 2 :(得分:0)

1。 我相信异常是在控制器中捕获的,因为UserDAO.addUser()是事务边界。

2。 DataIntegrityViolationException扩展NestedRuntimeException,因此您可以使用getMessage,getMostSpecificCause,getRootCause

3。 在你的情况下,我应该在你的控制器中捕获异常。您希望在可以处理它们的层中捕获异常。到目前为止,例如,如果我正在编写DAO方法来获取对象列表。我可能会检查DAO中的异常并返回一个空列表并记录错误vs抛出异常。