【问题标题】:Custom @Unique validator in Spring throws Hibernate Assertion failureSpring中的自定义@Unique验证器引发Hibernate Assertion失败
【发布时间】:2014-10-15 09:17:39
【问题描述】:

我制作了一个自定义的@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());
}

【问题讨论】:

  • 您的交易是如何组织的?我想usuario.persist() 写入数据库,那么如果是事务性对象呢?
  • 在该检查用户名请求时可能未创建会话。如果您使用 spring-security 这需要将 .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) 添加到 httpSecurity 配置。
  • 问题是,当用户名已经被使用时,它可以正常工作并在表单中显示错误消息,但是当用户名尚未被使用时,它会崩溃。 @px5x2,如果我使用的是 applicationContext-security.xml 文件,我应该在哪里添加 sessionCreationPolicy(...)?
  • @SergeBallesta,很抱歉,我不明白你的问题。
  • 您在休眠时出错。众所周知,Hibernate + spring 在尝试访问事务之外的数据库时会导致问题。我想这可能是你的错误的原因,但我需要知道你如何管理你的休眠事务。我认为这是休眠会话的问题,而不是 http 会话...

标签: java spring hibernate spring-roo validation


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

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

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

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

    【讨论】:

      【解决方案3】:

      出于某种原因必须成为习俗?不能只使用一个独特的约束?您可以将或放在您的脚本中或作为注释

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-04-21
        • 1970-01-01
        • 2018-02-22
        • 2021-04-20
        • 2023-02-11
        • 1970-01-01
        • 1970-01-01
        • 2019-06-25
        相关资源
        最近更新 更多