我有一个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,有没有人有更好的解决方案?
答案 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{}
}