2013-08-07 50 views
8

enter image description here在Spring MVC中,在那裏搭上數據庫異常

我下面的結構建議在以上(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 '[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>> 

我有以下問題:

  1. 爲什麼例外是由控制器(userService.addUser(user)com.bilitutor.cct.control.HomeController)抓住,而不是由DAO(sessionFactory.getCurrentSession().save(user);)然後冒泡到控制器?

  2. 我知道我得到一個org.springframework.dao.DataIntegrityViolationException,因爲我使用的是@Repository註釋,這可能是異常翻譯(糾正我,如果我錯了)。在那種情況下,當我發現異常時,如何找到它的錯誤代碼?

  3. 作爲一種最佳實踐,哪一層(DAO,服務或控制器)是捕獲異常的最佳位置?

相關類:

控制器:

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

    } 
} 

回答

0

1.

我懷疑你把你的控制器處理程序方法的事務邊界,例如是這樣的:一旦更新同步到數據庫

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

只能檢測到重複的行,這往往發生在事務關閉,如果你使用休眠/ jpa,因此你看它好像它是來自你的控制器

回想一下,@Transactional工作jdk代理/方面編織。

3.

答案取決於您的業務需求。發生特定異常時您需要做什麼?

+0

其實,它是我的服務層是Transactional(我已經更新了上面的帖子)。在例外情況下,我想找出異常的原因(因此我的帖子中的問題2)併發回相應的消息(電子郵件已存在,密碼不能爲空等)。 – Amarsh

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()看到包裝的異常

+0

非常感謝。這解釋了一切。感謝大家的幫助。 – Amarsh

+0

快速一...我嘗試捕捉服務層上的異常,但看起來我唯一可以捕捉它們的地方是在Controller中,這也許是Transaction Boundry之外的第一個攔截器。我在每個圖層上放置了一個try/catch塊,並且調試器僅在Controller層停止。這現在使得無法在服務層上捕獲和翻譯異常! – Amarsh

0

1. 我相信例外被卡在控制器UserDAO.addUser()是在事務範圍。

2. DataIntegrityViolationException延伸NestedRuntimeException所以你可以使用的getMessage,getMostSpecificCause,getRootCause

3. 在你的情況我上面,你應該捕獲該異常的控制器。你想在你可以處理它們的層上捕捉異常。到目前爲止,例如,如果我正在編寫一個DAO方法來獲取對象列表。我可能會檢查DAO中的異常並返回一個空列表並記錄錯誤並拋出異常。