FYI, feel free to suggest a better title for this. I have the following domain model (we have no control over this):
class Foo {
int id
String baz
Date someDate
static hasMany = [bars:Bar]
static mapping = {
id composite: [ "id", "baz" ]
}
}
class Bar {
int id
String baz
Date someDate
Foo foo
static mapping = {
id composite: ["id", "baz", "someDate"]
columns {
foo([:]) {
column name: "id"
column name: "baz"
}
}
}
}
My problem is: I have a List of Foos and I need to find all Bars where Foo.someDate == Bar.someDate, but only in one query instead of a query for each Foo, with no lazy loading involved. Also setting Bars to eager fetching is not an option.
For example if this is my data (pseudocode, I'm combining id and baz into simply "id" for simplicity):
[
Foo (someDate:4/1/2013)
|___ bars: [{someDate:12/4/2012, id:1}, {someDate:4/1/2013, id:2}]
Foo (someDate:5/10/2012)
|___ bars: [{someDate:{4/1/2013, id:3}
Foo (someDate:3/3/2013)
|___ bars: [{someDate:3/3/2013, id:4}, {someDate:9/5/2013, id:5}]
]
I need to return the Bars with id 2 and 4 in one query. I'm really not sure how to approach this.
It could be HQL if necessary but it's preferred to be a GORM query.
Try this:
Bar.executeQuery("select b from Bar b join b.foo as f where b.someDate = f.someDate and f in :fooList", [fooList: fooList])
Not sure if it is the best way to do so, but I think it will give you the result
:)
Bar.executeQuery("select b.id from Bar b, Foo f where b.someDate = f.someDate")
although I have removed your composite mapping constraints and then tried it, worked for me :)
or using where query:
def km = Bar.where {
foo.someDate==someDate
}
println(km.list())
If you already have foo list then can be filtered through findAll closure:
fooList.findAll{it.someDate in it.bars.someDate}
:)
Related
I have some grails Domain classes that have relationships between them like so...
Domain A {
...
Domain B
}
Domain B {
...
Domain C
}
Domain C {
...
String name
String attr1
String attr2
}
How can I use withCriteria to perform eager fetching on A such that instances of B and C that are related to A are included in my results
like so...
List<A> aList = [..., B: [..., C: [... name: 'name', attr1: 'attr1', attr2: 'attr2']]]
The way you describe the expected results is a peculiar and I can't tell exactly what you want there...
List<A> aList = [..., B: [..., C: [... name: 'name', attr1: 'attr1', attr2: 'attr2']]]
The declared type of aList suggest you want a List<A> but the value you show looks like a Map with some nested Maps.
If you execute a query like this...
def aList = A.withCriteria {
// your criteria goes here
}
What is returned from that will be a List of A and each A will reference a B and each B will reference a C.
def aList = A.withCriteria {
// your criteria goes here
}
for(A obj : aList) {
B b = obj.b
C c = b.c
// ...
}
I hope that helps.
EDIT BASED ON COMMENT:
It is unclear if you want them to always be eagerly fetched or if you want to express that when you express the query. It sounds like maybe you want to express it when you express the query, which can be done like this...
import import org.hibernate.FetchMode
// ...
A.withCriteria {
// criteria goes here
fetchMode "b", FetchMode.JOIN
fetchMode "b.c", FetchMode.JOIN
}
I'm wondering if its possible to create a grails domain object, but only have it persist on command as opposed as when we do operations on it.
To be more precise, this is what I have to do now:
Foo foo = new Foo(name:"asdf")
Bar bar = new Bar(name:"gzxj")
bar.save() // persist here
foo.save() // persist here
foo.addToBars(bar) // now I have to build the relationship
What I want:
Foo foo = new Foo(name:"asdf")
Bar bar = new Bar(name:"gzxj")
foo.addToBars(bar) // just build the relationship
bar.save() // would be great to ignore this step
foo.save() // can I now atomically build both objects and create the relationships?
My impressions is that the latter would be far faster if there are many relationships to associate. Am I really just wanting NoSQL here?
Depending on how you have your relationships set up, this is entirely possible. It really has nothing to do with what database you have implemented.
Parent class
class Foo {
String name
static hasMany = [bars: Bar]
}
Child class
class Bar {
String name
Foo foo //optional back reference
static belongsTo = Foo
}
Execution
Foo foo = new Foo(name: 'a')
Bar bar = new Bar(name: 'b')
foo.addToBars(bar)
foo.save()
//or even more terse
Foo foo = new Foo(name: 'c')
foo.addToBars(new Bar(name: 'd'))
foo.save()
The key is the belongsTo which defaults to a cascade of all. This can be explicitly set as well:
class Foo {
String name
static hasMany = [bars: Bar]
static mapping = {
bars cascade: 'all'
}
}
I know there has to be a GORMy way of doing this.
class A {
def foo
C c
}
class B {
def bar
C c
}
class C {
def x
def y
def z
}
I want to get all of C where A.foo == 'foo' and where B.bar == 'bar'.
I know I can do it from one or the other parent classes, eg:
def my_cs = A.withCriteria { eq('foo', 'foo') }*.c
...and I could separately grab all my B's where bar == 'bar' and loop through my_cs... but that seems inefficient and I feel like there must be a reasonable way to do it through the criteria syntax.
Is that possible, or is there another acceptable way of doing this?
Thanks.
-------- solution ----------
A.withCriteria {
createAlias('c.B', 'cb', CriteriaSpecification.LEFT_JOIN)
eq('foo', 'foo')
isNull('cb.c')
projections {
property 'c'
}
}
I found that I did not need to test bar for B.
If your C class doesn't have a reference back to As of Bs then I think getting the As and Bs separately and then combining them might be your only real option. However, I don't think it's really that bad.
If you want to make sure you have a collection of unique Cs then that's easy, too (no looping required). See http://groovy.codehaus.org/groovy-jdk/java/util/Collection.html#unique%28%29
e.g.
def my_cs = []
my_cs.addAll(A.withCriteria { eq('foo', 'foo') }*.c)
my_cs.addAll(B.withCriteria { eq('bar', 'bar') }*.c)
my_cs.unique(true) // Removes duplicates from the list
My domain contains a one-to-many relationship like this:
class A {
B b
}
class B {
String name
}
I want to create a criteria query on A which will look for A to have the B object with the given name. It may return multiple entries. So... compare a given string with the "name" field from B and return the list of entries of type A for which B matches the name.
Thx!
Unless I am missing something that should be pretty easy:
def instanceList = A.withCriteria {
b {
eq('name','whatever')
}
}
having the domain classes:
class A {
Date dateCreated
static hasMany = [b:B]
...
}
class B {
String name
String value
...
}
What createCriteria or HQL query can I use to return a list with:
A's creation dateB's value for A with the name entry set to 'X'
Note: Although there's no explicit constraint, there's only one "value" for each 'X' and a combination.
Thanks
The HQL would be
def results = A.executeQuery(
'select a.id, a.dateCreated, b from A a inner join a.b b ' +
'where b.name=:name',
[name: 'X'])
This will give you a List of 3-element Object[] arrays containing A.id, A.dateCreated, and the list of B instances. I added the id to the query so you can group by it client-side:
def grouped = results.groupBy { it[0] }
This will be a Map where the keys are the A ids and the values are the Lists from the original results.
Ideally you'd do the grouping at the database, but it would complicate the query, and assuming you don't have a large number of results it should be fast.