在Spring @Controller中回滚事务并在视图中显示异常消息

时间:2012-02-13 10:42:49

标签: spring spring-mvc transactions

我有一个Spring MVC控制器,当发生异常时,我想在视图中显示异常消息并回滚打开的事务。该视图包含如下形式:

<form:form method="POST" modelAttribute="registrationForm">
    <form:errors path="*" cssClass="error-message"/>
    ...
</form:form>

我想使用<form:errors ... />功能在视图中显示异常消息。这是我目前非常可怕的解决方案:

@RequestMapping(value = "/registration", method = RequestMethod.POST)
public ModelAndView submitForm(@ModelAttribute("registrationForm") RegistrationForm registrationForm,
                         BindingResult result,
                         ModelAndView modelAndView,
                         HttpServletRequest request) throws Exception
{
    registrationValidator.validate(registrationForm, result);

    if(result.hasErrors())
    {
        return setupForm(modelAndView, registrationForm);
    }
    else
    {
        try
        {
            // ... Some non-transactional operations...

            // The following operation is the only one annotated with @Transactional
            // myExampleDao is @Autowired, can throw exception
            myExampleDao.createFoo(bar);

            // ... Other non-transactional operations...

            return new ModelAndView("redirect:successful");
        }
        catch(Exception e)
        {
            throw new RegistrationException(e, registrationForm, result);
        }
    }
}

@ExceptionHandler(value = RegistrationException.class)
public ModelAndView registrationExceptionHandler(RegistrationException e) throws Exception
{
    RegistrationForm registrationForm = e.getRegistrationForm();
    BindingResult result = e.getBindingResult();

    result.reject("exception", e.getMessage());
    Map<String, Object> model = result.getModel();
    return setupForm(new ModelAndView("registration", model), registrationForm);
}

private ModelAndView setupForm(ModelAndView modelAndView, RegistrationForm registrationForm) throws Exception
{
    Map<String,Object> model = modelAndView.getModel();
    model.put("currentYear", Calendar.getInstance().get(Calendar.YEAR));
    return new ModelAndView("registration", model);
}

我遇到的问题是,当抛出异常时,事务不会回滚。

有人可以帮忙吗?

谢谢。

更新:略微改变了问题,以便更好地理解

更新:找到了一个非常糟糕的解决方案,可以在视图中显示异常消息。仍然面临抛出异常时未回滚的事务的问题。

更新:我在@Transactional方法中将@Transactional(rollbackFor = Exception.class)更改为MyExampleDao.createFoo(...),现在一切正常。这个解决方案仍然是丑陋的IMO,有没有人有更好的解决方案?

3 个答案:

答案 0 :(得分:3)

您的控制器中没有交易。将它们放在您的服务层中。

您可以创建一个抽象控制器类来实现异常处理(然后每个单独的控制器显然都会扩展):

public class AbstractCtrl {

    @Resource(name = "emailService")
    private EmailService emailService;

    /*
     * Default exception handler, catchs all exceptions, redirects to friendly
     * error page and send e-mail does not catch request mapping errors
     */

    @ExceptionHandler(Exception.class)
    public String myExceptionHandler(final Exception e) {
    final StringWriter sw = new StringWriter();
    final PrintWriter pw = new PrintWriter(sw);
    e.printStackTrace(pw);
    final String strStackTrace = sw.toString(); // stack trace as a string
    emailService.sendAlertMail(strStackTrace);

    return "exception"; // default friendly excpetion message for user
    }
}

但是,不要将事务放在控制器中,将它们放在服务层类中。

答案 1 :(得分:0)

没有人提出比我丑陋的解决方案更好的解决方案。这是我的解决方案解决了我的问题:

@RequestMapping(value = "/registration", method = RequestMethod.POST)
public ModelAndView submitForm(@ModelAttribute("registrationForm") RegistrationForm registrationForm,
                         BindingResult result,
                         ModelAndView modelAndView,
                         HttpServletRequest request) throws Exception
{
    registrationValidator.validate(registrationForm, result);

    if(result.hasErrors())
    {
        return setupForm(modelAndView, registrationForm);
    }
    else
    {
        try
        {
            // ... Some non-transactional operations...

            // The following operation is the only one annotated with @Transactional
            // myExampleDao is @Autowired, can throw exception
            myExampleDao.createFoo(bar);

            // ... Other non-transactional operations...

            return new ModelAndView("redirect:successful");
        }
        catch(Exception e)
        {
            throw new RegistrationException(e, registrationForm, result);
        }
    }
}

@ExceptionHandler(value = RegistrationException.class)
public ModelAndView registrationExceptionHandler(RegistrationException e) throws Exception
{
    RegistrationForm registrationForm = e.getRegistrationForm();
    BindingResult result = e.getBindingResult();

    result.reject("exception", e.getMessage());
    Map<String, Object> model = result.getModel();
    return setupForm(new ModelAndView("registration", model), registrationForm);
}

private ModelAndView setupForm(ModelAndView modelAndView, RegistrationForm registrationForm) throws Exception
{
    Map<String,Object> model = modelAndView.getModel();
    model.put("currentYear", Calendar.getInstance().get(Calendar.YEAR));
    return new ModelAndView("registration", model);
}

答案 2 :(得分:0)

您必须在控制器范围内管理事务的唯一方法是,从sessionFactory打开会话。

    Session session = sessionFactory.openSession();
try{
Transaction tx = session.beginTransaction();
    // code

session.save(foo);


tx.commit();
}catch(Exception e){
tx.rollback();
}finally{

try{session.close();}finally{}

}