I'm trying to write a criteria query in grails, for the following domains:
class Data {
Long createdById // this is user id
// other fields
}
class User {
Company company
// other fields
}
Now data, as the name suggest stores some data. It is not directly related to User but has a field createdById which is the id of User.
(I cannot give direct reference of User in Data because these belongs to different projects, where Data is from a custom reusable plugin.)
Now I want to write criteria on Data and list all records where it was created by users of a given company. that is data.id == User.id and user.company == givenCompany
In order to use any GORM/Hibernate query method (criteria, where, or HQL) you need an association between the domain classes. Since you can't make an association from Data
to User
, you cannot use GORM to write your query with your domain classes are they are. In other words, you need to use SQL; HQL (DomainClass.executeQuery()
will not work). But...
If you're willing to modify User
and maintain some redundant data, you can use HQL.
class User {
Company company
// other fields
/* Add uni-directional one-to-many */
static hasMany = [data: Data]
}
def data = AnyDomainClass.executeQuery('SELECT d FROM User as u INNER JOIN u.data AS d WHERE u.company = :company', [company: givenCompany])
The uni-directional one-to-many association creates a join table which would need to be maintained to reflect Data.createdById
. The HQL join syntax hints at the fact that associations are required. If you must use a criteria query...
To use a criteria query you'll need to modify User
and add an additional domain class. The reason is that a criteria query cannot project an association like HQL can. For example, this will not work:
def data = User.withCriteria {
eq('company', givenCompany)
projections {
property('data')
}
}
Due to not being able to modify Data
, this --which is the simplest solution-- will also not work:
def data = Data.withCriteria {
user {
eq('company', givenCompany)
}
}
So, you need a middle-man in order to project Data
instances:
class User {
Company company
// other fields
static hasMany = [userData: UserData]
}
class UserData {
Data data
static belongsTo = [user: User]
}
def data = User.withCriteria {
eq('company', givenCompany)
projections {
userData {
property('data')
}
}
}
As you can imagine, this method also requires some data maintenance.
Lastly, you have the option of using SQL and having the query return domain class instances. It boils down to something like this:
AnyDomainClass.withNewSession { session ->
def data = session.createSQLQuery('SQL GOES HERE').addEntity(Data).list()
}
For more information, check out my following articles: