query - grails tutorial
los criterios utilizan el enfoque de "unión interna" en lugar de la "unión izquierda" de manera predeterminada, lo que hace que mi consulta no funcione como lo planeé (3)
La pregunta es: ¿cómo puedo hacer que GORM genere uniones izquierdas en lugar de uniones internas en este ejemplo en particular?
Banco de pruebas:
Dadas las clases A, B y C:
class A{
B someObject
}
class B{
C importantObject
}
class C{
boolean interestingFlag
}
Quiero enumerar todos los elementos de una clase que:
- su objeto BC es nulo O
- su valor de interés en el objeto BC es falso
Lo que intenté hasta ahora:
Este enfoque produce una lista correcta de A donde BC es nulo (condicional 2 comentado) O una lista correcta de A donde BCinterestingFlag = falso (no importa si condicional 1 está comentado o no). Cuando ambos condicionales están sin comentar, solo devuelve una lista de elementos donde ABCinterestingFlag = false (ABC = null conditional se ignora)
// approach 1 (conditional 1 is ignored)
def result = A.withCriteria{
someObject{
or{
isNull(''importantObject'') // conditional 1, works well when conditional 2 is commented out
importantObject{
eq(''interestingFlag'', false) // conditional 2, works well alone, discards conditional 1 when both of them are uncommented
}
}
}
}
Edición: según lo solicitado en el comentario, estoy pegando un sql generado por hibernación:
Hibernate: select this_.id as id1_2_, this_.version as version1_2_,
this_.some_object_id as some3_1_2_, someobject1_.id as id2_0_,
someobject1_.version as version2_0_, someobject1_.important_object_id as
important3_2_0_, importanto2_.id as id0_1_, importanto2_.version as version0_1_,
importanto2_.interesting_flag as interest3_0_1_ from a this_
inner join b someobject1_ on this_.some_object_id=someobject1_.id
inner join c importanto2_ on someobject1_.important_object_id=importanto2_.id
where ((someobject1_.important_object_id is null or (importanto2_.interesting_flag=?)))
Cuando lo copio y pego en la herramienta de consulta pgAdmin directamente con algunas cosas cambiadas (las combinaciones internas cambiaron a las combinaciones de la izquierda, y siempre que el parámetro interestingFlag = "false") todo funciona como quería (obtengo ABC = null y ABCimportantFlag = objetos falsos)
Hibernate: select this_.id as id1_2_, this_.version as version1_2_,
this_.some_object_id as some3_1_2_, someobject1_.id as id2_0_,
someobject1_.version as version2_0_, someobject1_.important_object_id as
important3_2_0_, importanto2_.id as id0_1_, importanto2_.version as version0_1_,
importanto2_.interesting_flag as interest3_0_1_ from a this_
left join b someobject1_ on this_.some_object_id=someobject1_.id
left join c importanto2_ on someobject1_.important_object_id=importanto2_.id
where ((someobject1_.important_object_id is null or (importanto2_.interesting_flag=false)))
Pude lograr uniones externas izquierdas solo con HQL
Class Transaction {
String someProperty
static hasMany = [reviews: Review]
static hasOne = [reviewQueue: ReviewQueue]
}
Class ReviewQueue {
Transaction transaction
Boolean isComplete
Boolean isReady
}
Class Review {
Transaction transaction
String reviewResult
}
def list = Transaction.executeQuery(
"select t.id, rq.isReady, rq.isComplete, count(r.transaction.id) " +
"from Transaction t " +
"join t.reviewQueue rq " +
"left outer join t.reviews r " +
"where rq.isComplete = false " +
"and rq.isReady = true " +
"group by t.id " +
"having count(r.transaction.id) = 0 " +
"order by rq.transaction.id ",
[max: 10, offset: 0])
Solución probada y de trabajo:
def result = A.withCriteria{
createAlias(''someObject'', ''so'', CriteriaSpecification.LEFT_JOIN)
createAlias(''so.importantObject'', ''imp'', CriteriaSpecification.LEFT_JOIN)
or {
isNull(''so.importantObject'')
eq(''imp.interestingFlag'', false)
}
}
Usa la combinación izquierda para lograr esto. Esto debería funcionar, pero no lo he probado en vivo.
def result = A.withCriteria{
someObject {
createAlias("importantObject", "io", CriteriaSpecification.LEFT_JOIN)
or{
isNull(''importantObject'') // conditional 1
eq(''io.interestingFlag'', false) // conditional 2
}
}
}
Eidt: basado en este post: http://grails.1312388.n4.nabble.com/CriteriaBuilder-DSL-Enhancements-td4644831.html esto también debería funcionar:
def result = A.withCriteria{
someObject {
isNull(''importantObject'') // conditional 1
importantObject(JoinType.LEFT) {
eq(''interestingFlag'', false) // conditional 2
}
}
}
Por favor, publique sus resultados en ambas soluciones.
Edición 2: esta es una consulta que es exactamente una situación que ha descrito, pero difiere de sus ejemplos. Debe comenzar desde aquí, use showSql para depurar los SQL generados y jugar con las combinaciones izquierdas.
def result = A.withCriteria{
or {
isNull(''someObject'')
eq(''someObject.importantObject.interestingFlag'', false)
}
}