Search code examples
javaspringhibernatespring-roovalidation

Custom @Unique validator in Spring throws Hibernate Assertion failure


I made a custom @UniqueNombre validator to check if an user name already exists in the database and, when it is not repeated and tries to persist the data it throws an Hibernate exception:

ERROR org.hibernate.AssertionFailure - HHH000099: an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session): org.hibernate.AssertionFailure: null id in es.cesga.cloudpyme2.openinnovation.Usuario entry (don't flush the Session after an exception occurs)

The code of the validator is:

public class UniqueNombreValidator implements ConstraintValidator<UniqueNombre, String> {

    @Override
    public void initialize(UniqueNombre paramA) {
    }

    @Override
    public boolean isValid(String nombre, ConstraintValidatorContext ctx) {
        return (Usuario.countFindUsuariosByNombreEquals(nombre) == 0);
    }

}

And the controller:

@RequestMapping(method = RequestMethod.POST, produces = "text/html")
public String create(@Valid Usuario usuario, BindingResult bindingResult, Model uiModel, HttpServletRequest httpServletRequest) {
    if (bindingResult.hasErrors()) {
        populateEditForm(uiModel, usuario);
        return "usuario/create";
    }
    uiModel.asMap().clear();
    setUpUsuario(usuario, httpServletRequest);
    usuario.persist();
    return "redirect:/usuario/" + encodeUrlPathSegment(usuario.getIdUsuario().toString(), httpServletRequest);
}

Without the check for a repeated user name, the controller works like charm. Any ideas?

UPDATE: Here is the method that is used inside the validator:

public static Long Usuario.countFindUsuariosByNombreEquals(String nombre) {
    if (nombre == null || nombre.length() == 0) throw new IllegalArgumentException("The nombre argument is required");
    EntityManager em = Usuario.entityManager();
    TypedQuery q = em.createQuery("SELECT COUNT(o) FROM Usuario AS o WHERE o.nombre = :nombre", Long.class);
    q.setParameter("nombre", nombre);
    return ((Long) q.getSingleResult());
}

Solution

  • I cannot be sure without the full stacktrace, but it looks like you are trying to use the EntityManager outside of a transaction. It is declared static in the Usuario class, and called through a static method. But unless you declared a transaction in your controller (which would be uncommon), none is opened when you call em.createQuery(...), so Hibernate throws an exception.

    As you only do readonly operation, a simple workaround is to use the open session in view pattern with an OpenSessionInViewFilter. It allows for a (normally readonly) session to be disponible all along a request processing.