Grails 2.x createCriteria 'or' doesn't work for nested associations
Asked Answered
C

1

5

It seems that in Grails 2.x, if you have a domain class association, and you try to run a createCriteria using or on that relation + another query, the or will ignore the other query and just use the results of the nested association. I realize this may be a little confusing, so here is an example:

class Passenger {
    Long id
    Boolean isDriving
}

class Car {
    Long id
    Passenger passenger
    Boolean isMoving

    static constraints = {
        passenger nullable: true
    }
}

and a test:

class CarIntegrationTests {
    @Test
    void testCar() {
    Passenger passenger1 = new Passenger(isDriving: true)
    passenger1.save()

    Car car1 = new Car(passenger: passenger1, isMoving: false)
    Car car2 = new Car(isMoving: true)

    car1.save()
    car2.save()

        def queryResults = Car.createCriteria().list() {
            or {
                eq('isMoving', true)// This by itself works

                passenger {// And this by itself works
                    eq('isDriving', true)
                }
            }// But OR'd, it only returns the results of the nested part
        }

        assertEquals 2, queryResults.size() // Returns 1
    }
}

This same code worked in older versions of Grails, but doesn't not seem to work now -- does anyone know of a good workaround to this, other than running multiple queries?

Convexity answered 15/10, 2013 at 20:45 Comment(2)
Try turning on hibernate logging and see what sql is being generated. As a work-around you could just write an hql query, but you shouldn't have toHairball
@JimSosa Yeah I'll try it with logging. I was hoping not to use HQL, but yeah, I might have to.Convexity
I
11

UPDATE:
Post Grails 2.x, Criteria by default uses inner join, but for this particular case outer join has to be used as passenger association will not allow to follow or condition if it is an inner join and passenger is not set to car.

import org.hibernate.Criteria

def queryResults = Car.createCriteria().list() {
    createAlias('passenger', 'passenger', Criteria.LEFT_JOIN)
    or {
        eq('isMoving', true)
        eq('passenger.isDriving', true)
    }
}
Ifc answered 15/10, 2013 at 21:16 Comment(2)
@GrailsGuy See my update. Outer join has to be used.Ifc
Note by grails 3/hibernate 4+, Criteria is deprecated and should be JoinType.LEFT_OUTER_JOIN instead, but otherwise still a perfect answer.Rocketry

© 2022 - 2024 — McMap. All rights reserved.