SELECT查询的Hibernate ConstraintViolationException

时间:2012-12-30 11:38:09

标签: hibernate hql unique-constraint transactional

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

我实现了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

为什么呢?

3 个答案:

答案 0 :(得分:3)

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

您必须部分初始化新实体,将它们与会话限制的实体相关联,现在尝试执行查询。 Hibernate会话在查询之前执行flush,并且使用ConstraintViolationException失败,这是正常的。

解决方案:

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

答案 1 :(得分:0)

我怀疑PersistClass中的getter或setter不是普通的get / set逻辑。 如果你的getter没有返回Hibernate传递给setter的同一个实例,那么Hibernate会刷新" dirty"交易完成后的实体。

您可以粘贴PersistClass代码,也可以粘贴Hibernate日志来验证吗?

答案 2 :(得分:0)

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

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