GORM createCriteria и list не возвращают одинаковые результаты: что я могу сделать?
Я использую Nimble и Shiro для своих систем безопасности, и я только что натолкнулся на ошибку GORM. В самом деле:
User.createCriteria().list {
maxResults 10
}
возвращает 10 пользователей, тогда как User.list(max: 10)
возвращает 9 пользователей!
После дальнейших расследований я узнал, что createCriteria
возвращает дважды одного и того же пользователя (администратора), потому что администратор имеет 2 роли!!! (Я не шучу).
Похоже, что любой пользователь с более чем 1 ролью будет возвращен дважды в createCriteria
позвонить и User.list
вернусь max-1
экземпляры (т.е. 9 пользователей вместо 10 пользователей)
Какой обходной путь можно использовать, чтобы вернуть 10 уникальных пользователей?
Это очень раздражает, потому что я не могу правильно использовать нумерацию страниц.
Мои доменные классы:
class UserBase {
String username
static belongsTo = [Role, Group]
static hasMany = [roles: Role, groups: Group]
static fetchMode = [roles: 'eager', groups: 'eager']
static mapping = {
roles cache: true,
cascade: 'none',
cache usage: 'read-write', include: 'all'
}
}
class User extends UserBase {
static mapping = {cache: 'read-write'}
}
class Role {
static hasMany = [users: UserBase, groups: Group]
static belongsTo = [Group]
static mapping = { cache usage: 'read-write', include: 'all'
users cache: true
groups cache: true
}
}
6 ответов
Менее сжатый и понятный, но использование HQL-запроса кажется способом решения этой проблемы. Как описано в документации Grails (раздел executeQuery), параметры paginate могут быть добавлены в качестве дополнительных параметров для executeQuery.
User.executeQuery("select distinct user from User user", [max: 2, offset: 2])
Таким образом, вы все еще можете использовать критерии и передавать параметры списка / пагинации
User.createCriteria().listDistinct {
maxResults(params.max as int)
firstResult(params.offset as int)
order(params.order, "asc")
}
РЕДАКТИРОВАТЬ: нашел способ получить оба! Полностью собираюсь использовать его сейчас
http://www.intelligrape.com/blog/tag/pagedresultlist/
If you call createCriteria().list() like this
def result=SampleDomain.createCriteria().list(max:params.max, offset:params.offset){
// multiple/complex restrictions
maxResults(params.max)
firstResult(params.offset)
} // Return type is PagedResultList
println result
println result.totalCount
У вас будет вся необходимая информация в хорошем формате PagedResultList!
/РЕДАКТИРОВАТЬ
К сожалению, я не знаю, как получить комбинацию полных результатов И подмножества максимального / смещения нумерации страниц в одном вызове. (Кто-нибудь, кто может просветить об этом?)
Я могу, однако, говорить об одном способе, которым я с успехом пользовался, чтобы заставить нумерацию страниц работать вообще в Grails.
def numResults = YourDomain.withCriteria() {
like(searchField, searchValue)
order(sort, order)
projections {
rowCount()
}
}
def resultList = YourDomain.withCriteria() {
like(searchField, searchValue)
order(sort, order)
maxResults max as int
firstResult offset as int
}
Это пример того, что я использую, чтобы начать нумерацию страниц. Как сказал КоК выше, я все еще в растерянности из-за одного атомарного утверждения, которое дает оба результата. Я понимаю, что мой ответ более или менее совпадает с KoK, извините, но я думаю, что стоит отметить, что rowCount() в проекциях немного более понятен для чтения, и у меня пока нет прав на комментарии: /
Наконец: это Святой Грааль (без каламбура) из ссылок на использование критериев гибернации Грааля; добавьте в закладки;) http://www.grails.org/doc/1.3.x/ref/Domain%20Classes/createCriteria.html
Оба решения, предложенные здесь Рубеном и Аароном, до сих пор не "полностью" работают на разбиение на страницы, потому что возвращаемый объект (от executeQuery() и listDistinct) является ArrayList (с максимальным количеством объектов в нем), а не PagedResultList со свойством totalCount заполнен, как я и ожидал, для "полностью" поддержки нумерации страниц.
Скажем, пример немного сложнее в этом: Предположим, что Role имеет дополнительный атрибут rolename AND b. мы хотим вернуть только отдельные объекты User с Role.rolename, содержащие строку "a" (имея в виду, что пользователь может иметь несколько ролей с rolename, содержащим строку "a")
Чтобы сделать это с 2 запросами, мне нужно сделать что-то вроде этого:
// First get the *unique* ids of Users (as list returns duplicates by
// default) matching the Role.rolename containing a string "a" criteria
def idList = User.createCriteria().list {
roles {
ilike( "rolename", "%a%" )
}
projections {
distinct ( "id" )
}
}
if( idList ){
// Then get the PagedResultList for all of those unique ids
PagedResultList resultList =
User.createCriteria().list( offset:"5", max:"5" ){
or {
idList.each {
idEq( it )
}
}
order ("username", "asc")
}
}
Это кажется крайне неэффективным.
Вопрос: есть ли способ выполнить оба вышеперечисленных с помощью одного оператора GORM/HQL?
Спасибо за то, что поделились своей проблемой, и Коком за то, что ответили. У меня не было возможности переписать его на HQL. Вот мое решение (обходной путь): http://ondrej-kvasnovsky.blogspot.com/2012/01/grails-listdistinct-and-pagination.html
Пожалуйста, скажите мне, если это полезно (по крайней мере, для кого-то).
Ты можешь использовать
User.createCriteria().listDistinct {
maxResults 10
}