Hibernate Criteria API - как сделать заказ по размеру коллекции? - PullRequest
4 голосов
/ 10 августа 2009

При условии, что у меня есть классы User и UserGroup. Существует необязательная группа «1-много» для ассоциации пользователей, и сопоставление сопоставляется с обеих сторон (сторона UserGroup через свойство, называемое «members», которое является HashSet, а сторона User через свойство «group»).

Как использовать Criteria API, как запросить все группы, отсортированные по количеству членов группы?

(Правка) Я должен был указать, когда спросил, что нумерация страниц выполняется в SQL, поэтому упорядочение также должно выполняться в SQL, если это возможно.

Ответы [ 3 ]

4 голосов
/ 28 августа 2012

Это невозможно по умолчанию с использованием 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.

1 голос
/ 31 августа 2009

Другим вариантом будет использование аннотации @Formula, которая в основном эквивалентна созданию вложенного выбора, чтобы вы могли рассчитывать.

В свойстве / элементе данных вашего объекта БД (который вы должны создать) добавьте аннотацию к получателю, например:

@ Формула ("(ВЫБЕРИТЕ СЧЕТ (TABLE_NAME.ID) ИЗ ТАБЛИЦЫ ГДЕ все, что ...)") public long getNewProperty { вернуть newProperty; }

Затем нацельтесь на члена, чтобы получить значения вашего счета ...

Примечание: имейте в виду, что при указании sql в аннотации формулы необходимо указывать имена полей непосредственно при обращении к текущей таблице объектов (this), поскольку hibernate сам с этим справляется. Так что в вашем предложении WHERE просто используйте ID или что-то само по себе при обращении к текущей таблице объектов, которая, как я подозреваю, является UserGroup.

Мне нужно было установщик для моего нового свойства, чтобы hibernate работал и не давал компилятору жалоб.

Могут быть и другие умные способы использования @Formula, но я довольно новичок в этом, и, похоже, не очень много документации по этому вопросу ...

Если вы никогда раньше не использовали вложенные SELECTS, возможно, вам стоит сначала пересоздать в sql - это мне помогло.

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

0 голосов
/ 10 августа 2009

Вероятно, вы могли бы сделать это, определив «производное свойство» для вашей сущности, которое будет доступно только для чтения и возвращает размер коллекции внутри этой сущности. Самый простой способ определить производное свойство - задокументировано здесь :

обновить, вставить (необязательно - по умолчанию true): указывает, что сопоставленный столбцы должны быть включены в SQL ОБНОВЛЕНИЕ и / или ВСТАВКА. Установка обоих в ложь позволяет чистый «производное» свойство, значение которого инициализируется из некоторого другого свойства который отображается на тот же столбец (столбцы), или триггер или другое приложение.

Как только свойство будет определено, вы сможете использовать имя свойства как условие заказа в API критериев, как и любое другое свойство. Я сам этого не пробовал.

Если это сработает, то, скорее всего, это будет сделано в виде сортировки в памяти, поскольку он не сможет сопоставить его с SQL. Если это проблема, то по той же ссылке выше описано, как реализовать производное свойство с помощью пользовательского фрагмента SQL:

Получена мощная функция свойства. Эти свойства по определение только для чтения. Недвижимость значение вычисляется во время загрузки. Вы объявить вычисление как SQL выражение. Это затем переводит в Подзапрос предложения SELECT в SQL запрос, который загружает экземпляр:

...