GORM createCriteria и список не возвращают одинаковые результаты: что мне делать?

Я использую 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 
  } 
} 

person fabien7474    schedule 11.10.2010    source источник
comment
Какова ваша реализация, наконец? Потому что у меня такая же проблема. Большое спасибо   -  person Jonathan Lebrun    schedule 09.03.2011


Ответы (6)


Менее лаконично и ясно, но использование HQL-запроса кажется способом решить эту проблему. Как описано в документации Grails (раздел executeQuery), параметры разбивки на страницы можно добавить в качестве дополнительных параметров. выполнитьЗапрос.

User.executeQuery("select distinct user from User user", [max: 2, offset: 2])
person Ruben    schedule 11.10.2010
comment
Большое спасибо. Оно работает !! Вы знаете, есть ли проблема JIRA, заполненная для этой ошибки? - person fabien7474; 12.10.2010
comment
Подробное объяснение того, почему listDistinct отфильтровывает только дубликаты в памяти, можно найти здесь, в FAQ по Hibernate: >community.jboss.org/wiki/ . - person Ruben; 12.10.2010

таким образом, вы все еще можете использовать критерии и передавать параметры списка/разбивки на страницы

User.createCriteria().listDistinct {
    maxResults(params.max as int)
    firstResult(params.offset as int)
    order(params.order, "asc")
}
person Aaron Saunders    schedule 11.10.2010
comment
речь идет не только о предоставлении параметров разбиения на страницы, но и о наличии реального PagedResultList с набором свойств totalCount... - person codewandler; 15.09.2015

РЕДАКТИРОВАТЬ: Нашел способ получить оба! Полностью собираюсь использовать его сейчас

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() в прогнозах немного понятнее для чтения, и у меня пока нет прав комментировать:/

Наконец: это Святой Грааль (без каламбура) ссылок на использование критериев hibernate; добавьте его в закладки;) http://www.grails.org/doc/1.3.x/ref/Domain%20Classes/createCriteria.html

person Will Buck    schedule 17.11.2011

Оба решения, предложенные здесь Рубеном и Аароном, по-прежнему не «полностью» работают для разбивки на страницы, потому что возвращаемый объект (из executeQuery() и listDistinct) представляет собой ArrayList (с максимальным количеством объектов в нем), а не PagedResultList со свойством totalCount заполняется, как я и ожидал, для «полной» поддержки нумерации страниц.

Допустим, пример немного сложнее в том, что: a. предположим, что роль имеет дополнительный атрибут имени роли И b. мы хотим вернуть отдельные объекты User с именем Role.rolename, содержащим строку «a» (имея в виду, что у пользователя может быть несколько ролей с именем роли, содержащим строку «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?

person Koka    schedule 22.11.2010
comment
вы можете использовать «in» id idList вместо цикла и idEq (it) - person Jonathan Lebrun; 09.03.2011

Ты можешь использовать

User.createCriteria().listDistinct {
    maxResults 10
}
person Ruben    schedule 11.10.2010
comment
Почти работает :)ListDistinct вернет 9 пользователей вместо 10 (удаление дублированного пользователя). Но как я могу использовать разбиение на страницы, например, с параметрами смещения. - person fabien7474; 11.10.2010
comment
Это может быть полезно: grails.org/doc/latest/api/grails. /форма/ - person Matt Lachman; 11.10.2010
comment
Проблема с отдельной проекцией заключается в том, что вы сохраняете только «столбцы» или свойства, для которых указана отдельная проекция. - person Ruben; 11.10.2010

Спасибо, что поделились своей проблемой, и Кок за ответ. У меня не было возможности переписать его на HQL. Вот мое решение (обходной путь): http://ondrej-kvasnovsky.blogspot.com/2012/01/grails-listdistinct-and-pagination.html

Пожалуйста, скажите мне, если это полезно (хотя бы для кого-то).

person Ondrej Kvasnovsky    schedule 20.01.2012