API критериев возвращает слишком маленький набор результатов

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

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!

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


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

Ответы

Ответ 1

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

Используя довольно простое сопоставление SaleItem one-to-many (которое, мы надеемся, само собой разумеющееся), такой 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). A ResultTransformer like 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() не существует, я только что закончил. Возможно, это должно быть!

Ответ 3

Другим решением является следующее:

  • запустите criteria.list() без установки псевдонима = > ссылочные наборы/список корневой сущности будут заполнены прокси = > здесь вы правильно установите максимальные результаты и такие
  • запускайте критерии псевдонимов самостоятельно в одном сеансе спящего режима = > указанные выше прокси будут инициализированы

Что-то вроде этого:

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;

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

Ответ 4

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

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;
}

Ответ 5

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

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