如何使用hibernate重用Criteria对象?

时间:2008-12-15 16:42:29

标签: java hibernate spring-mvc pagination displaytag

我正在尝试使用hibernate和displaytag执行查询结果分页,而Hibernate DetachedCriteria对象正在尽力阻止它们。让我解释一下......

使用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对象并让它在theese线上运行......

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对象来解决这个问题,但这就像重新发明另一个轮子一样。有没有更聪明的方法,可能会复制最初传入的Criteria并处理该副本?

更新 : 看起来首先调用getList,然后多次调用getFullListSize,因此,一旦传入排序,getFullListSize将失败。只打一次db(在getList我说)并缓存结果,而不需要复制/重置Criteria对象,但仍然......

更新(再次) : 忘掉这一点,一旦我完成了count我无法做select,反之亦然。我真的需要两个不同的Criteria对象。

7 个答案:

答案 0 :(得分:47)

Criteria.setProjection(null);
Criteria.setResultTransformer(Criteria.ROOT_ENTITY);

将有效地“重置”rowCount投影和标准本身执行之间的标准。

我会确保在执行rowCount之前没有添加您的订单,它会减慢速度。我的PaginatedList ALWAYS实现在查找结果之前运行计数查询,因此排序不是问题。

答案 1 :(得分:2)

好吧,DetachedCriteria是Serializable,所以你有内置(如果不优雅)深度克隆支持。您可以在构造时将初始条件序列化为byte [],然后在每次要使用它时对其进行反序列化。

答案 2 :(得分:2)

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 hibernate。无论如何,这http://forum.hibernate.org/viewtopic.php?t=939039

论坛帖子应该能够回答你的问题。

答案 3 :(得分:1)

丑陋,因为它可能是我最终使用序列化技巧。我只是在构造DetachedCriteria对象时将PaginatedList对象序列化为字节数组,并在需要时对其进行反序列化。哎哟。

答案 4 :(得分:0)

另一件值得尝试的事情:

实现类似the one suggested on hibernate's site的通用DAO,并将其与Restrictions对象一起传递给PaginatedList对象。然后,PaginatedList对象将执行类似

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

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

尚未尝试过,但它应该有效。

答案 5 :(得分:0)

有一种更好,更简单的克隆条件的方法,只需简单地:

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

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

再回到您的问题。对于分页,我制作了一种方法,结果得到了我

1. Total rows count
2. Rows filtered by page & pageSize
In a single query to DB.
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;

答案 6 :(得分:-1)

public static DetachedCriteria Clone(this DetachedCriteria criteria)
{
   var dummy = criteria.ToByteArray();
   return dummy.FromByteArray<DetachedCriteria>();
}