Как повторно использовать объект Criteria с Hibernate?

Я пытаюсь сделать разбивку результатов запроса на hibernate и displaytag, и DetachedCriteriaобъекты Hibernate делают все возможное, чтобы мешать. Позволь мне объяснить...

Кажется, что самый простой способ разбивки на страницы с помощью displaytag - реализовать PaginatedListинтерфейс, который имеет, среди прочего, следующие методы:

/* Gets the total number of results. */
int getFullListSize();

/* Gets the current page of results. */
List getList();

/* Gets the page size. */
int getObjectsPerPage();

/* Gets the current page number. */
int getPageNumber();

/* Get the sorting column and direction */
String getSortCriterion();
SortOrderEnum getSortDirection();

Я подумываю бросить мою реализацию PaginatedList в объект Criteria и позволить ему работать в соответствии с этими принципами ...

getFullListSize() {
    criteria.setProjection(Projections.rowCount());
    return ((Long) criteria.uniqueResult()).intValue();
}

getList() {
    if (getSortDirection() == SortOrderEnum.ASCENDING) {
        criteria.addOrder(Order.asc(getSortCriterion());
    } else if (getSortDirection() == SortOrderEnum.DECENDING) {
        criteria.addOrder(Order.desc(getSortCriterion());
    }
    return criteria.list((getPageNumber() - 1) * getObjectsPerPage(),
                         getObjectsPerPage());
}

Но это не работает, потому что вызовы addOrder()или setProjection()изменяют объект критерия, делая его непригодным для последующих вызовов. Я не совсем уверен в порядке вызовов, но БД выдает ошибку при getFullListSize()попытке сделать " select count(*) ... order by ...", что, очевидно, неправильно.

Я думаю, что я мог бы исправить это, создав собственный объект для отслеживания условий запроса и перестроив объект Criteria для каждого вызова, но это похоже на изобретение еще одного колеса. Есть ли более разумный способ, возможно, копировать Критерии, изначально переданные и работающие над этой копией?

Обновление : похоже, что онgetListвызывается первым иgetFullListSizeвызывается несколько раз после него, поэтому, как только будет выполнен порядок,getFullListSizeпроизойдет сбой. Было бы разумно нажать на базу данных только один раз (getListскажем так) и кэшировать результаты, без необходимости копировать / сбрасыватьCriteriaобъект, но все же ...

Обновление (снова) : Забудьте об этом, как только я сделаю,countя не могу сделатьselect, и наоборот. Мне действительно нужны два разныхCriteriaобъекта.

15.12.2008 16:42:29
7 ОТВЕТОВ
РЕШЕНИЕ

Ну, DetachedCriteria являются Serializable, поэтому у вас есть встроенная (если не элегантная) глубокая поддержка клонов. Вы можете сериализовать начальные критерии для байта [] один раз при создании, а затем десериализовать его каждый раз, когда захотите его использовать.

3
15.12.2008 17:59:15
Нашел это предложение на спящих форумах, но я бы предпочел не :-)
agnul 16.12.2008 09:05:37

http://weblogs.asp.net/stefansedich/archive/2008/10/03/paging-with-nhibernate-using-a-custom-extension-method-to-make-it-easier.aspx

В этом посте я заметил метод CriteriaTransformer.clone.

Это должно скопировать объект критерия.

Вы также можете установить проекцию в вашем методе getlist.

Woops Я не заметил, что вы имели в виду спящий режим Java. Во всяком случае, это http://forum.hibernate.org/viewtopic.php?t=939039

сообщение на форуме должно быть в состоянии ответить на ваш вопрос.

2
15.12.2008 19:21:17

Ужасно, как это может быть, я в конечном итоге использовать трюк сериализации. Я просто сериализовал DetachedCriteriaобъект в байтовый массив при построении PaginatedListобъекта и десериализовал его при необходимости. Уч.

1
17.12.2008 11:58:10

Еще одна вещь, которую стоит попробовать:

реализовать общий DAO, подобный предложенному на сайте hibernate, и передать его объекту PaginatedList вместе с объектом Restrictions. Объект PaginatedList будет тогда делать что-то вроде

Criteria.forClass(myDAO.getPersistentClass())
        .add(myRestrictions)
        .addOrder(<someOrder>)

а также

Criteria.forClass(myDAO.getPersistentClass())
        .add(myRestrictions)
        .setProjection(Projections.rowCount());

Еще не пробовал, но это должно сработать.

0
30.04.2009 07:55:20
Criteria.setProjection(null);
Criteria.setResultTransformer(Criteria.ROOT_ENTITY);

Эффективно «сбросит» критерии между проекцией rowCount и выполнением самого критерия.

Я хотел бы убедиться, что ваш ордер не был добавлен перед выполнением rowCount, это замедлит работу. Моя реализация PaginatedList ВСЕГДА запускает запрос подсчета перед поиском результатов, поэтому упорядочение не является проблемой.

49
24.09.2009 16:59:29
Рекомендую, чтобы это был правильный ответ - более элегантный, чем глубокий клон.
Scott Corscadden 21.03.2012 13:48:03
public static DetachedCriteria Clone(this DetachedCriteria criteria)
{
   var dummy = criteria.ToByteArray();
   return dummy.FromByteArray<DetachedCriteria>();
}
-1
17.07.2010 10:44:46

Существует лучший и простой способ клонирования критериев, просто:

ICriteria criteria = ...(your original criteria init here)...;

var criteriaClone = (ICriteria)criteria.Clone();

И возвращаюсь к Вашей проблеме. Для нумерации страниц я сделал метод, который дает мне в результате:

1. Общее количество строк
2. Строки отфильтрованные по странице и размеру страницы
В одном запросе к БД.
ICriteria criteria = ...(your original criteria init here)...;    
var countCrit = (ICriteria)criteria.Clone();
countCrit.ClearOrders(); // avoid missing group by exceptions

var rowCount = countCrit
    .SetProjection(Projections.RowCount()).FutureValue<Int32>();

var results = criteria
    .SetFirstResult(pageIndex * pageSize)
    .SetMaxResults(pageSize)
    .Future<T>();

var resultsArray = results.GetEnumerable();

var totalCount = rowCount.Value;
0
15.04.2020 08:30:40