Hibernate Criteria API - как сделать заказ по размеру коллекции?
Предполагая, что у меня есть классы User и UserGroup. Существует необязательная группа "1-много" для ассоциации с пользователем, и сопоставление сопоставляется с обеих сторон (сторона UserGroup через свойство, называемое "members", которое является HashSet, а сторона User через свойство "group").
Используя API Критерии, как я могу сделать запрос для всех групп, отсортированных по количеству членов группы?
(Изменить) Я должен был указать, когда я спросил это, что нумерация страниц выполняется в SQL, поэтому упорядочение также должно выполняться в SQL, если это возможно.
3 ответа
Другим вариантом будет использование аннотации @Formula, которая в основном эквивалентна созданию вложенного выбора, чтобы вы могли рассчитывать.
В свойстве / элементе данных вашего объекта БД (который вы должны создать) добавьте аннотацию к получателю, например:
@Formula ("(ВЫБЕРИТЕ COUNT(TABLE_NAME.ID) ИЗ ТАБЛИЦЫ ГДЕ как угодно...)") public long getNewProperty{ return newProperty; }
Затем выберите целевой элемент напрямую, чтобы получить ваши значения счетчика...
Примечание. Имейте в виду, что при указании sql в аннотации формулы необходимо указывать имена полей непосредственно при обращении к текущей таблице объектов (this), поскольку hibernate сам с этим справляется. Так что в вашем предложении WHERE просто используйте ID или что-то само по себе при обращении к текущей таблице объектов, которая, как я подозреваю, является UserGroup.
Мне нужно было установщик для моего нового свойства, чтобы Hibernate работал и не давал компилятору жалоб.
Могут быть и другие умные способы использования @Formula, но я новичок в этом, и, похоже, документации по этому вопросу не так много...
Если вы никогда раньше не использовали вложенные SELECTS, возможно, вам стоит сначала воссоздать в sql - это мне помогло.
Надеюсь это поможет
По умолчанию это невозможно с помощью API Citeria, но вы можете расширить класс org.hibernate.criterion.Order. Эта статья о том, как расширить этот класс: http://blog.tremend.ro/2008/06/10/how-to-order-by-a-custom-sql-formulaexpression-when-using-hibernate-criteria-api
Мое решение:
public class SizeOrder extends Order {
protected String propertyName;
protected boolean ascending;
protected SizeOrder(String propertyName, boolean ascending) {
super(propertyName, ascending);
this.propertyName = propertyName;
this.ascending = ascending;
}
public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery) throws HibernateException {
String role = criteriaQuery.getEntityName(criteria, propertyName) + '.' + criteriaQuery.getPropertyName(propertyName);
QueryableCollection cp = (QueryableCollection) criteriaQuery.getFactory().getCollectionPersister(role);
String[] fk = cp.getKeyColumnNames();
String[] pk = ((Loadable) cp.getOwnerEntityPersister())
.getIdentifierColumnNames();
return " (select count(*) from " + cp.getTableName() + " where "
+ new ConditionFragment()
.setTableAlias(
criteriaQuery.getSQLAlias(criteria, propertyName)
).setCondition(pk, fk)
.toFragmentString() + ") "
+ (ascending ? "asc" : "desc");
}
public static SizeOrder asc(String propertyName) {
return new SizeOrder(propertyName, true);
}
public static SizeOrder desc(String propertyName) {
return new SizeOrder(propertyName, false);
}
}
Метод toSqlString основан на классе org.hibernate.criterion.SizeExpression. (Источник: http://javasourcecode.org/html/open-source/hibernate/hibernate-3.6.0.Final/org/hibernate/criterion/SizeExpression.java.html).
Пример использования:
hibernateSession.createCriteria(UserGroup.class).addOrder( SizeOrder.asc("members") ).list();
здесь будут перечислены группы пользователей, упорядоченные по размеру элементов по возрастанию, где "members" - это коллекция User в сущности UserGroup.
Вероятно, вы могли бы сделать это, определив "производное свойство" для вашей сущности, которое будет доступно только для чтения и возвращает размер коллекции внутри этой сущности. Самый простой способ определить производное свойство задокументировано здесь:
update, insert (необязательно - по умолчанию true): указывает, что сопоставленные столбцы должны быть включены в операторы SQL UPDATE и / или INSERT. Установка обоих в false допускает чистое "производное" свойство, значение которого инициализируется из некоторого другого свойства, которое отображается на тот же столбец (столбцы), или триггером или другим приложением.
Как только свойство будет определено, вы сможете использовать имя свойства как условие заказа в API критериев, как и любое другое свойство. Я сам этого не пробовал.
Если это сработает, скорее всего, это будет сделано в виде сортировки в памяти, поскольку он не сможет сопоставить его с SQL. Если это проблема, то по той же ссылке выше описано, как реализовать производное свойство с помощью пользовательского фрагмента SQL:
Мощная особенность - производные свойства. Эти свойства по определению только для чтения. Значение свойства вычисляется во время загрузки. Вы объявляете вычисление как выражение SQL. Затем это преобразуется в подзапрос предложения SELECT в запросе SQL, который загружает экземпляр: