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
}
Другие вопросы по тегам