Spring中的自定义@Unique验证器抛出Hibernate Assertion失败

时间:2014-10-15 09:17:39

标签: java spring hibernate spring-roo validation

我创建了一个自定义的@UniqueNombre验证器来检查数据库中是否已存在用户名,并且当它没有重复并尝试保留数据时会抛出Hibernate异常:

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)

验证器的代码是:

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

}

控制器:

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

如果没有检查重复的用户名,控制器的工作方式就像魅力一样。有什么想法吗?

更新:以下是验证程序中使用的方法:

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());
}

3 个答案:

答案 0 :(得分:2)

如果没有完整的堆栈跟踪,我无法确定,但看起来您正试图在事务之外使用EntityManager。它在Usuario类中声明为static,并通过静态方法调用。但是除非你在你的控制器中声明了一个事务(这种情况并不常见),所以当你调用em.createQuery(...)时没有打开,所以Hibernate会抛出异常。

由于您只进行只读操作,因此一个简单的解决方法是使用OpenSessionInViewFilter在视图模式中使用打开会话。它允许(通常只读)会话在请求处理过程中都是可以取消的。

答案 1 :(得分:0)

我认为您的验证器在为实体分配了正确的标识符值之前触发,因此您获得了异常。

我不建议在验证器中运行查询。无论如何,数据库总是更好地强制执行这种类型的规则。您的应用程序逻辑应尽量避免此类情况,因此第一步是在保存数据之前运行查询。

应尽快向用户报告异常,以防止失败事务可能导致的进一步操作。一旦会话抛出异常,您可能不再使用它,因为它可能处于不一致的状态。

答案 2 :(得分:0)

出于任何原因必须是一个习惯?不能只使用一个独特的约束?您可以将脚本放入脚本或作为注释

   @Column(name = "usuario",unique=true)
   String usuario;