5

I am trying to implement the registration controller for a Rest API. I have read about where to place @Transactional quite a bit. (Not at DAO level but at the services maybe orchestrated). In my use case I want not only services but also a hibernate validation to use the same transaction.

This is the code of the controller:

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

I have written an custom contraint annotation, which validates an attribute of the parameter registerIO. Both, this validator and userService.register(registerIO); access the database (check if the email address is already in use).

Therefore I want both methods use the same Hibernate session and transaction.

This approach results in the following exception:

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)

The problem is the @Transactional annotation. When I place this annotation at the methods witch call the database everything works find but two transactions are startet. I suspect that when i place it at the register Method the hibernate validation is performed before @Transactional starts the transaction for this method.

I developed the following functional workaround but I am not happy with it. This codes does not use the @Valid annotation but calls the validator by itself:

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

I try to summarise my question: Using Spring MVC and Hibernate-Validation together with @Valid and @Transactional, how is it possible to encapsulate the whole request into one transaction?

Thank you :)

Hardy
  • 18,659
  • 3
  • 49
  • 65
Sebastian
  • 424
  • 8
  • 25
  • are you using JSR 330 validations? – Pravat Panda May 20 '13 at 23:06
  • JSR 330: Dependency Injection for Java: I am using Spring for my dependency injection. @Autowire is working fine in this situation. I am not sure if you mean JSR 303: Bean Validation: Here I am using the Hibernate-Validator 4.3.1 implementation – Sebastian May 20 '13 at 23:14
  • To answer this side question. Hibernate Validator is the reference implementation of the Bean Validation (JSR 330) specification. – Hardy May 21 '13 at 09:36

1 Answers1

1

Your workaround could be improved by using a single Validator and injecting it intp the controller. Have you tried:

@Autowired
private Validator validator;

This way you skip the overhead of creating the validator on each request. You should also be careful with race conditions. While you are checking the database whether a given email exists another request can create this record, so that you still get an exception at the time you insert the data.

Hardy
  • 18,659
  • 3
  • 49
  • 65
  • 1
    The workaround improvement works fine. Oh, I have not thought about that particular race conditions. My thought was: Combining validation and the controller execution in one transaction would solve all problems like "lost update" or would at least throw an exception/rollback. Checking for an unused email does not produce a read/write conflict in this situation. Maybe I should ask how to do this in an other question and do some more reading. Thanks :) – Sebastian May 23 '13 at 10:00