【问题标题】:Criteria API returns a too small resultsetCriteria API 返回的结果集太小
【发布时间】:2011-01-12 02:54:15
【问题描述】:

这怎么可能,我必须遵循标准

Criteria criteria = getSession().createCriteria(c); criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY); criteria.add(Restrictions.eq("active",true)); List list = criteria.list();

列表的大小现在是 20。如果我在条件中添加一个最大结果,

Criteria criteria = getSession().createCriteria(c);
criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
criteria.setMaxResults(90);
criteria.add(Restrictions.eq("active",true));
List list = criteria.list();

.. 现在列表的大小是 18!

我不明白定义最大结果后结果集大小如何变小,因为行数小于定义的最大值。这肯定看起来像一个错误,还是我不知道休眠的一些奇怪方面?


如果您正在寻找此问题的答案,请务必阅读已接受的答案及其 cmets。

【问题讨论】:

  • 可以添加hibernate映射和执行的SQL查询吗?
  • Thomas,很遗憾没有,因为它是一个相当复杂和深度的数据结构——即使对于相关的 POJO 也是如此。我会尝试将问题隔离到一个较小的测试用例中,但您的回答很可能是解决我问题的关键。
  • 是的,但我想知道为什么它适用于纯 HQL 查询(我们改用 Criteria API)。我不太喜欢说明它可能适用于 Hibernate 的未来版本的文档,但是没有提到该文档的更新时间,所以我不知道这个案例是否仍然有效,因为它适用于HQL。
  • 据我所知问题没有解决。
  • 好问题。正是我需要的!

标签: java hibernate criteria hibernate-criteria


【解决方案1】:

通过在 Hibernate 中打开 SQL 调试并比较生成的查询,可以非常清楚地看到这里发生了什么。

使用相当简单的SaleItem 一对多映射(希望不言自明),基于Criteria 的查询如下:

Criteria c = sessionFactory.getCurrentSession().createCriteria(Sale.class);
c.createAlias("items", "i");
c.add(Restrictions.eq("i.name", "doll"));
c.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
c.setMaxResults(2);

产生这样的 SQL:

select top ? this_.saleId as saleId1_1_, ... 
from Sale this_ 
inner join Sale_Item items3_ on this_.saleId=items3_.Sale_saleId 
inner join Item items1_ on items3_.items_id=items1_.id 
where items1_.name=?

Query 是这样的:

Query q = sessionFactory.getCurrentSession().createQuery("select distinct s from Sale s join s.items as i where i.name=:name");
q.setParameter("name", "doll");
q.setMaxResults(2);

产生类似的东西:

select top ? distinct hibernated0_.saleId as saleId1_ 
from Sale hibernated0_ 
inner join Sale_Item items1_ on hibernated0_.saleId=items1_.Sale_saleId 
inner join Item hibernated2_ on items1_.items_id=hibernated2_.id 
where hibernated2_.name=?

请注意第一行 (DISTINCT) 的区别。像DISTINCT_ROOT_ENTITY 这样的ResultTransformer 是一个Java 类,它在执行SQL之后处理SQL 行的结果。因此,当您指定 maxResults 时,它将作为 SQL 的行限制应用; SQL 包括对Collection 中元素的连接,因此您将 SQL 结果限制为 90 个子元素。一旦应用了DISTINCT_ROOT_ENTITY 转换器,这可能会导致少于 20 个根元素,这完全取决于在 90 个连接结果中哪些根元素碰巧最先出现。

DISTINCT 在 HQL 中的行为非常不同,因为它实际上使用了 SQL DISTINCT 关键字,该关键字应用在行限制之前。因此,这符合您的预期,并解释了两者之间的区别。

理论上,您应该查看setProjection 以在SQL 级别应用投影——类似于c.setProjection(Projections.distinct(Projections.rootEntity()))——但不幸的是Projections.rootEntity() 不存在,我只是编造的。也许应该!

【讨论】:

  • 对于这个问题是否有任何已知(有效)的解决方法?这显然使 Criteria API 对我们无法使用 :(
  • 不是一个好的,我知道。如果你不处理大量的原始数据,你总是可以全部选择它们,使用DISTINCT_ROOT_ENTITY,然后取出List的前n项。不,不可扩展,是的,相当糟糕。不幸的是,没有更好的建议——除非其他人知道任何其他可能性,否则在这种特殊情况下,Criteria API 可能不适合您。
  • 这就是问题所在,使用动态注入标准的查询,大量数据.. sigh
  • 对于任何寻找此问题答案的人,我通过使用两个查询来解决问题。首先,我有我想要的所有限制和限制的正常标准,但添加了投影标准。 setProjection(Projections.distinct(Projections.id()));有了这个标准,我得到了我想要获取的 pojos 的 id 列表。在此之后,我使用 Restrictions.in("id",ids); 创建了另一个标准。第二个条件包含 DISTINCT_ROOT_ENTITY,但不包含 setMaxResult(因为它应用于第一个查询)。
  • 不错。使用 2 个查询有点棘手,但这与您将获得的一样好。很好的解决方案。
【解决方案2】:

【讨论】:

    【解决方案3】:

    希望对你有帮助

    public List<Employee> getData(int to, int from) {
    
        Criteria hCriteria = null;
        List<Employee> viewDataList = null;
        List<Employee> exactDataList = null;
        try {
    
            hSession = HibernateSessionFactory.getSession();
            hTransaction = hSession.beginTransaction();
            hCriteria = hSession.createCriteria(Employee.class);
    
            /*
            hCriteria.setFirstResult(to);
            hCriteria.setFirstResult(from);
            */
            hCriteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
            viewDataList = hCriteria.list();
    
            // for limit
            exactDataList=viewDataList.subList(from,to);
    
            hTransaction.commit();
        } catch (Exception e) {
            hTransaction.rollback();
    
        } finally {
            try {
                hSession.flush();
                HibernateSessionFactory.closeSession();
            } catch (Exception hExp) {
            }
    
    }
    
        return exactDataList;
    }
    

    【讨论】:

      【解决方案4】:

      另一种解决方案如下:

      1. 在不设置任何别名的情况下运行criteria.list() => 根实体的引用集/列表将填充代理 => 在这里您正确设置了最大结果等
      2. 在同一个休眠会话中自行运行别名条件 => 上述代理将被初始化

      类似这样的:

      Criteria criteria = this.getSession().createCriteria(User.class);
      criteria.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
      criteria.setMaxResults(10);
      
      // First get the results without joining with the other tables
      List<User> results = criteria.list();
      
      // at this point the set roles is filled with proxies
      // we'll now create and execute the join so these proxies are filled since we're still in the same session
      getSession().createCriteria(User.class, "u")
              .createAlias("u.roles", "r", CriteriaSpecification.LEFT_JOIN)
              .list();
      
      return results;
      

      希望对你有帮助,
      斯蒂金

      【讨论】:

        【解决方案5】:

        这是休眠中的一个已知问题。查看@Cowan 以获取生成的SQL 和问题的解释。在他们的 jira 中有一个开放的错误请求。让我们希望有人来解决它:)

        https://hibernate.atlassian.net/browse/HB-520

        【讨论】:

          猜你喜欢
          • 2012-06-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-05-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多