Hibernate验证和Spring控制器的一个事务

时间:2013-05-20 22:07:08

标签: java spring-mvc hibernate-validator transactional hibernate-4.x

我正在尝试为Rest API实现注册控制器。我已经了解了@Transactional的位置。 (不是在DAO级别,但在服务可能是精心策划的)。在我的用例中,我不仅需要服务,还需要使用hibernate验证来使用相同的事务。

这是控制器的代码:

@Autowired
private UserService userService;

@RequestMapping(method = RequestMethod.GET)
@ResponseBody
@Transactional
public DefaultResponse register(@Valid RegisterIO registerIO, BindingResult errors) {
    DefaultResponse result = new DefaultResponse();

    if (errors.hasErrors()) {
        result.addErrors(errors);
    } else {
        userService.register(registerIO);
    }

    return result;
}

我编写了一个自定义约束注释,它验证了参数registerIO的属性。这两个验证器和userService.register(registerIO);访问数据库(检查电子邮件地址是否已被使用)。

因此我希望两种方法都使用相同的Hibernate会话和事务。

此方法导致以下异常:

org.hibernate.HibernateException: No Session found for current thread
org.springframework.orm.hibernate4.SpringSessionContext.currentSession(SpringSessionContext.java:97)
org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:941)

问题是@Transactional注释。当我将这个注释放在方法中时,调用数据库的一切都可以找到,但是两个事务都是startet。我怀疑当我将它放在寄存器方法时,在@Transactional启动此方法的事务之前执行休眠验证。

我开发了以下功能性解决方法,但我对它不满意。此代码不使用@Valid注释,而是单独调用验证器:

@RequestMapping(method = RequestMethod.GET)
@ResponseBody
@Transactional
public DefaultResponse register( RegisterIO registerIO, BindingResult errors) {
    DefaultResponse result = new DefaultResponse();

    ValidatorFactory vf =  Validation.buildDefaultValidatorFactory();
    Validator validator = vf.getValidator();
    Set<ConstraintViolation<RegisterIO>> valResult = validator.validate(registerIO);

我试着总结一下我的问题: 将Spring MVC和Hibernate-Validation与@Valid和@Transactional一起使用,如何将整个请求封装到一个事务中?

谢谢:)

1 个答案:

答案 0 :(得分:1)

通过使用单个Validator并将其注入控制器中,可以改善您的解决方法。你试过了吗?

@Autowired
private Validator validator;

这样就省去了在每个请求上创建验证器的开销。 你也应该小心竞争条件。当您检查数据库是否存在给定的电子邮件时,另一个请求可以创建此记录,以便在插入数据时仍然会出现异常。