Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

One Transaction for Hibernate Validation and Spring controller

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 :)

like image 941
Sebastian Avatar asked Nov 12 '22 05:11

Sebastian


1 Answers

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.

like image 116
Hardy Avatar answered Nov 15 '22 10:11

Hardy