GORM createCriteria с отношением один ко многим
С тех пор, как пару часов я пытаюсь понять, как создать критерий с критерием, который должен быть в списке.
Если я уменьшу свой код, у меня будут следующие два класса домена:
Отель - базовый класс домена, который я хочу получить:
class Hotel {
static hasMany = [rooms: Room, amenities: HotelAmenity]
}
HotelAmenity - В отеле есть список удобств.
class HotelAmenity {
String name
}
# 1 Мой первый подход был примерно таким:
PagedResultList pgl = Hotel.createCriteria().list(max: limit, offset: offset) {
// ..
and {
amenities {
'in'("id",myLongIdsList)
}
}
Это работает. Но в этом случае мне возвращают каждый отель, в котором есть хотя бы одна из указанных анемий. Но моя желанная цель - найти отели, которые имеют все указанные удобства.
# 2 Итак, я попытался следовать
// ..
amenities {
condition.hotelAmenities.collect { it.toLong() }.each {
and {
eq('id', it)
}
}
Но этот код выглядит так, как будто он портит набор результатов, потому что он всегда возвращает пустой список, если определено более одной функции. Между прочим, в этом сценарии с одной услугой все отели, которые содержит мой список результатов, не будут иметь никаких других удобств в своем списке удобств, кроме того, который я искал - даже при том, что многие из них назначены. Я совершенно не понимаю этого поведения.
# 3 Еще один подход, с которым я пытался поиграть listDistinct
вместо list
, Но я больше не пытался это делать, когда понял, что не получаю PagedResultList
но список hotel domain objects
вернулся. И поэтому у меня не будет totalCount
параметр (который мне нужен для моей функциональности разбивки на страницы)
Итак, я застрял между написанием собственного запроса и / или решением этой проблемы с помощью подхода GORM. Может также случиться так, что я немного запутался в том, как именно это должно работать.
Для любого уведомления или решения я был бы очень рад.
Спасибо Кристофер
PS: я использую Grails версии 1.3.7
PPS: Если какая-либо информация отсутствует, пожалуйста, дайте мне знать.
РЕДАКТИРОВАТЬ
Это решение, с которым я пришел вместе:
Map sqlParams = [:]
String mySql = "SELECT SQL_CALC_FOUND_ROWS hotel.id FROM hotel WHERE "
if(hotelAmenities.size()>0) {
String commaSeperatedAmenities = hotelAmenities.toString()
// the amenity IDs as comma seperates list so we can use them in the `IN` statement (ie 1,3,6,7)
commaSeperatedAmenities = commaSeperatedAmenities.substring(1,commaSeperatedAmenities.length()-1)
mySql += '( SELECT COUNT(hotel_amenities_id) from hotel_hotel_amenity ' +
'WHERE hotel_amenities_id = hotel.id AND hotel_amenity_id IN ' +
'(' + comaAmentities + ') ) = ' + condition.hotelAmenities.size()
}
mySql += 'LIMIT :offset, :limit'
sqlParams.put("offset",offset)
sqlParams.put("limit",limit)
def mresult = sqlInstance.rows(mySql,sqlParams)
List<Hotel> hotels = Hotel.getAll(mresult.collect {it.id})
int calcFoundRows = sqlInstance.rows("SELECT FOUND_ROWS()").get(0).get("FOUND_ROWS()").toString().toInteger()
2 ответа
Построить критерии на лету:
def neededAmenities = ...
PagedResultList pgl = Hotel.createCriteria().list(max: limit, offset: offset) {
// ..
amenities {
and {
neededAmenities.each { needed ->
idEq (needed.id)
}
}
}
}
В чистом SQL это не намного проще:
where exists(amenities.id == XXX) and exists(amenities.id == YYY) and (...) ...
Вы могли бы пойти, вероятно, более оптимально с intersect
запрос с семантикой, как это:
where intersect(
select id from amenities where ...,
(:neededAmenities))
= (:neededAmenities)
Некоторый образец здесь.
редактировать: я пытался построить критерии второго типа и потерпел неудачу - я не вижу способа построить having count
состояние Во всяком случае, это будет медленнее, чем несколько in
-с как это придется count
Amenities
для всех hotels
,
ОБНОВЛЕНИЕ, это должно дать вам список отелей, которые имеют все удобства, попробуйте это:
def amenitySize = HotelAmenity.list()?.size()
def hotelList = Hotel.createCriteria().list{
sizeEq("amenities", amenitySize)
}