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 и сравнивая сгенерированные запросы.
Используя довольно простое сопоставление Sale
→ Item
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()
не существует, я только что закончил. Возможно, это должно быть!
Ответ 2
SetMaxResults не работает с внешними запросами SQL. Возможно, это ваша проблема: Hibernate не возвращает отличные результаты для запроса с внешней привязкой объединения, включенной для коллекции (даже если я использую отдельное ключевое слово)?.
Ответ 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