Criteria API возвращает слишком маленький набор результатов - PullRequest
14 голосов
/ 02 февраля 2010

Как это возможно, я должен по следующим критериям

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);
<b>criteria.setMaxResults(90);</B>
criteria.add(Restrictions.eq("active",true));
List list = criteria.list();

.. теперь размер списка 18!

Я не понимаю, как размер наборов результатов может быть меньше после определения максимальных результатов, так как количество строк меньше, чем определенный максимум. Это похоже на ошибку или есть какие-то странные аспекты гибернации, о которых я не знаю?


Если вы ищете ответ на этот вопрос, обязательно прочитайте принятый ответ и его комментарии.

Ответы [ 5 ]

9 голосов
/ 03 февраля 2010

То, что здесь происходит, можно очень четко увидеть, включив отладку SQL в Hibernate и сравнив сгенерированные запросы.

Используя довольно простое 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). ResultTransformer подобно DISTINCT_ROOT_ENTITY - это класс Java, который обрабатывает результаты строк SQL после выполнения SQL. Поэтому, когда вы указываете maxResults, это будет применяться как ограничение строки в SQL; SQL включает соединение с элементами Collection, поэтому вы ограничиваете свой результат SQL до 90 подэлементов После применения преобразователя DISTINCT_ROOT_ENTITY это может привести к появлению менее 20 корневых элементов, в зависимости от того, какие корневые элементы окажутся первыми в 90 соединенных результатах.

DISTINCT в HQL ведет себя очень по-разному, в том смысле, что фактически используется ключевое слово SQL DISTINCT, которое применяется до ограничения строки. Таким образом, это ведет себя так, как вы ожидаете, и объясняет разницу между 2.

Теоретически вы должны смотреть setProjection, чтобы применить проекцию на уровне SQL - что-то вроде c.setProjection(Projections.distinct(Projections.rootEntity())) - но, к сожалению, Projections.rootEntity() не существует, я только что придумал. Возможно, так и должно быть!

2 голосов
/ 02 февраля 2010
1 голос
/ 04 июля 2012

Надеюсь, что это может помочь

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;
}
1 голос
/ 29 ноября 2010

Другое решение заключается в следующем:

  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;

Надеюсь, это поможет,
Стейн

0 голосов
/ 06 ноября 2013

Это известная проблема в спящем режиме. Посмотрите на @Cowan для сгенерированного SQL и объяснение проблемы. Существует открытый запрос об ошибке в их jira. Будем надеяться, что кто-то придет и исправит это:)

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

...