【问题标题】:Hibernate ConstraintViolationException on SELECT querySELECT 查询上的 Hibernate ConstraintViolationException
【发布时间】:2012-12-30 11:38:09
【问题描述】:

我有一个带有多字段唯一约束的持久类。但是定义的唯一约束对我来说是不够的,因为在其中一个字段不相等但相似的值也是唯一的。

我实现了一个checkUniqueConstraint 方法。 在 DAO 类的添加和更新方法中,我在添加或更新持久对象之前调用了checkUniqueConstraint

checkUniqueConstraint 方法只需运行一个 SELECT 查询来查找与输入类似的对象,并在找到一些对象时抛出一个检查异常。

public class PersistClassDao {    
public void checkUniqueConstraint(PersistClass persistObject) throws DuplicateEntityException {
    /**
    * creating a query string that find persist objects similar to input parameter.
    **/
    try {
        PersistClass result = (PersistClass) query.uniqueResult();
        if(result != null && (persistObject.getId() == null  || persistObject.getId() != result.getId())){
            throw new DuplicateEntityException(exceptionMessage, "");
        } 
    } catch (NonUniqueResultException e) {
        throw new DuplicateEntityException(exceptionMessage);
    }
}

public long add(/* field of new persistObject*/) throws DuplicateEntityException {
    //creates a transisent instance of persistObject
    PersistClass newObject = getPersistClass(/* field of new persistObject*/);
    checkUniqueConstraint(newObject);
    try {
        getCurrentSession().save(newObject);
        getCurrentSession().flush();
        return newObject.getId();
    } catch (ConstraintViolationException e) {
        throw new DuplicateEntityException();
    }
}

我有一个用于添加的事务服务方法和另一个用于更新的方法。

当我运行我的测试时

  • 首先添加2个persistObjects(通过服务方法)。
  • 然后更新其中一个(通过服务方法),使其与另一个相似
  • 我希望抛出 DuplicatException

但实际上是 org.hibernate.exception。通过 checkUniqueConstraint 抛出 ConstraintViolationException!!

为什么?

【问题讨论】:

  • 向我们展示导致异常的代码,以及异常堆栈跟踪。你说你通过服务方法更新一个对象,但是服务方法添加一个新对象。
  • 很难说问题出在哪里,但从我的角度来看,您正在尝试实现一些不希望的事情。我真的建议您仔细考虑您的数据库模型并尝试将您的约束放入数据库中,而不是用 Java 解决它。
  • @JBNizet 你是对的,这是一个 API defect 但实际上我们的服务方法更新了一个现有的持久对象(通过获取持久对象 id 为输入参数)

标签: hibernate hql unique-constraint transactional


【解决方案1】:

问题一定是在FlushMode。默认情况下,Hibernate 在执行查询之前会刷新实体,请参阅 http://docs.jboss.org/hibernate/core/3.5/javadocs/org/hibernate/FlushMode.html#AUTO

您必须已部分初始化新实体,将它们与会话绑定的实体相关联,然后尝试执行查询。 Hibernate 会话在查询之前执行刷新并失败并出现 ConstraintViolationException,这没关系。

解决方案:

  1. 在整个应用程序的 cfg 文件中将 FlushMode 更改为 COMMIT
  2. 通过 session.setFlushMode(FlushMode.COMMIT)
  3. 只修改一个会话
  4. 您可以通过调用 query.setFlushMode(FlushMode.COMMIT) 将更改的影响缩小到单个 Query。

【讨论】:

    【解决方案2】:

    如果在select 之前有任何insert / update 操作,Hibernate 只会在select 之前刷新。默认情况下,当调用saveupdate 时,Hibernate 不会立即将更改提交到数据库。这是为了利用 JDBC 批处理来提高性能。 Hibernate 只会在会话 关闭事务提交select 发送到数据库时刷新对数据库的更改。 select 之前的刷新是必要的,因为如果没有将先前的更改刷新到数据库,查询结果可能不正确。

    因此,您的插入/更新实际上是在 find 方法之前调用的对象创建/更新,并且仅在 select 之前被刷新到 DB。

    【讨论】:

      【解决方案3】:

      我怀疑 PersistClass 中的 getter 或 setter 不是简单的 get/set 逻辑。 如果您的 getter 没有返回 Hibernate 传递给 setter 的相同实例,Hibernate 将在事务完成时刷新“脏”实体。

      您能否粘贴您的 PersistClass 代码,也许还可以粘贴 Hibernate 日志来验证这一点?

      【讨论】:

      • 是的。我有一对非普通的 getter setter 方法,但它们对应于瞬态字段。
      猜你喜欢
      • 2023-03-07
      • 2013-08-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-09
      • 1970-01-01
      • 2014-08-01
      相关资源
      最近更新 更多