Grails per-query one-to-one eager fetching - grails

Let's say you have face and nose, and you want to get the nose based on a faceId.
The Grails user guide tells you how to use mapping = { fetch:join } in a domain class to eagerly fetch the nose in one query whenever you fetch the face.
But I don't want to eagerly fetch all the time. I just want to do on this particular case to use one query instead of two. Can this be done on a per-query basis? Is there some way to do something like:
Face.get(faceId, [join:nose])?

Do you mean somthing link taht:
static fetchMode = [nose:'eager']
found here
Update:
You can solve the problem with a criteria query. Here you can set the fetch mode in the query:
import org.hibernate.FetchMode as FM
def c = MyDomain.createCriteria()
def results = c.list {
maxResults(10)
firstResult(50)
fetchMode("aRelationship", FM.EAGER)
}

Related

Avoiding subqueries in HQL using Grails

I have two object, a room type and a reservation. Simplified they are:
class Room {
String description
int quantity
}
class Reservation {
String who
Room room
}
I want to query for all rooms along with the number of rooms available for each type. In SQL this does what I want:
select id, quantity, occupied, quantity-coalesce(occupied, 0) as available
from room left join(select room_id, count(room_id) as occupied from reservation)
on id = room_id;
I'm not getting anywhere trying to work out how to do this with HQL.
I'd appreciate any pointers since it seems like I'm missing something fairly fundamental in either HQL or GORM.
The problem here is your trying to represent fields that are not your domain classes like available and occupied. Trying to get HQL\GORM to do this can be a bit a little frustrating, but not impossible. I think you have a couple options here...
1.) Build your domain classes so that there easier to use. Maybe your Room needs to know about it's Reservations via a mapping table or, perhaps write what you want the code to look like and then adjust the design.
For example. Maybe you want your code to look like this...
RoomReservation.queryAllByRoomAndDateBetween(room, arrivalDate, departureDate);
Then you would implement it like this...
class RoomReservation{
...
def queryAllByRoomAndDateBetween(def room, Date arrivalDate, Date departureDate){
return RoomReservation.withCriteria {
eq('room', room)
and {
between('departureDate', arrivalDate, departureDate)
}
}
}
2.) My second thought is... It's okay to use the database for what it's good for. Sometimes using sql in you code is simply the most effective way to do something. Just do it in moderation and keep it centralized and unit tested. I don't suggest you use this approach because you query isn't that complex, but it is an option. I use stored procedures for things like 'dashboard view's' that query millions of objects for summary data.
class Room{
...
def queryReservations(){
def sql = new Sql(dataSoruce);
return sql.call("{call GetReservations(?)}", [this.id]) //<-- stored procedure.
}
}
I'm not sure how you can describe a left join with a subquery in HQL. INn any case you can easily execute raw SQL in grails too, if HQL is not expressive enough:
in your service, inject the dataSource and create a groovy.sql.Sql instance
def dataSource
[...]
def sql= new Sql(dataSource)
sql.eachRow("...."){row->
[...]
}
I know it's very annoying when people try to patronize you into their way of thinking when you ask a question, instead of answering your question or just shut up, but in my opinion, this query is sufficiently complex that I would create a concept for this number in my data structure, perhaps an Availability table associated to the Room, which would keep count not only of the quantity but also of the occupied value.
This is instead of computing it every time you need it.
Just my $.02 just ignore it if it annoys you.

Grails : how to customize order with a sql function?

I want to retrieve the 10 nearest geolocalized objects using Gorm.
To do so, I would like to customize the order() parameter in order to use a get_distance(longitude, latitude, :longitude, :latitude) sql function.
I've been struggling with this all day, does anybody have a hint ?
Thanks !
EDIT
I finally managed to do what I wanted but with a very ugly solution :
I added a sqlProjection in which I put my get_distance() function and was able to order by it.
Adding a projection removes the retrieval of the object properties so I had to explicitly ask for it by adding property projections and I managed to do it with introspection.
Then I had to define the result transformer of the criteria for it to give me domain instances.
If register your function with the hibernate SQL dialect, then you can use it in HQL queries. For example, put this in your bootstrap code:
import org.hibernate.dialect.function.SQLFunctionTemplate
import org.hibernate.Hibernate
def dialect = applicationContext.sessionFactory.dialect
def getDistance = new SQLFunctionTemplate(Hibernate.INTEGER, "get_distance(?1,?2)")
dialect.registerFunction('get_distance', getDistance)
Then you can use it in HQL:
Location.executeQuery(
"""
select id, get_distance(latitude, longitude) as distance
from Location
order by distance
""",
[], [max: 10])
What we did is the following:
Create the query with whatever DB specific operators, but as a view
Create a new domain class object solely to map to the view, thereby allowing you to do GORM criteria queries and the like

grails sum one-to-many

I need a help with what I believe is very trivial stuff.
class User {
String name
static hasMany = [files : File]
}
class File {
long size
}
Now when I need the total size of all files a given user has I use the following which is working :
def user = User.get(id)
user.files.each(total+=it.size)
but how ugly is it, when I am sure it can be done with simple select sum query with either plain SQL or GORM/CRITERIA
I have tried something like File.sumBySize..()
Or
def c = File.createCriteria()
def f = c.list{
eq("user", user) // What here ?
projections
{
sum("size")
}
}
I dont know how to specify the parent(user) relationship which is not defined in File class but in Grails join tables
Any help appreciated
Why is that ugly? It only takes 1 statement (with no SQL at all) to sum up all the file sizes. Though if you want, you may be able to use HQL like so:
def user = User.get(id)
def sum = File.executeQuery("select sum(f.size) from File f where f.user = ?", user);
But I think you'd need to add a belongsTo to File to do that. There's probably a simpler HQL/GORM method that's just not coming to my mind at the moment, but honestly, I don't think there's anything easier than what you already did.
You can do it with an HQL query, but it has a small problem with the name of the 'size' field
(appears to be a reserved word). If you rename it to 'length', 'fileSize', etc. then this will work:
User.executeQuery(
'select sum(file.length) from User u join u.files file where u.id=:userId',
[userId: id])[0]
A more elegant way to sum the file sizes in Groovy code is:
def total = user.files.sum {it.size}
But if you can calculate the file size totals in the query that returns the users, that's the way I'd do it.

Grails Many-To-Many - Problems of dynamic finder

I hope you can help me guys. Google unfortunately didn't helps me out and my search here at stackoverflow didn't as well :-(
I have two DomainClasses HumanResource and Task with a many-to-many relationship.
Model-Definitions:
Task:
class Tasks {
String name
static belongsTo = [HumanResource]
static hasMany = [humanResources: HumanResource]
//also tried but didn't help -> static fetchMode = [humanResources:"eager"]
}
HumanResource:
class HumanResource {
String name
static hasMany = [tasks: Tasks]
}
I also tried to add an index on the id-field with mapping={} but I also think that's not the solution, it didn't help and I think there is already an index on the id-field.
So, what I did and not works is now to find all human resources for the given tasks! And the tasks comes from Services and they are already fetched in the service model with "static fetchMode = [tasks:"eager"]"!
Controller-Code:
def listHumanResourcesFromTasks = {
def list = HumanResource.findAllByTasks(service.getTasks())
//and I tried also with an own HashMap but didn't work as well
}
I always get an error "org.springframework.dao.InvalidDataAccessResourceUsageException" with an SQL-GrammarException. But I really don't know why. The "service.getTasks()" objects are fully filled (as I wrote with fetchMode = [tasks:"eager"])...
It would be awesome if somebody could give me the winning hint.
Thanks a lot for your time.
Best wishes,
Marco
This sort of query isn't supported - you'd need to use HQL or a criteria query in general. But this particular one is easy since you have a bidirectional relationship. You can get all of the HumanResource instances for a collection of Tasks with this:
def resources = service.getTasks().collect { it.humanResources }.flatten() as Set
It needs to be a Set since the same HumanResource instance may appear multiple times so you need to condense the List into unique instances.

How to order by more than one field in Grails?

Is there a way to get a list ordered by two fields, say last and first names?
I know .listOrderByLastAndFirst and .list(sort:'last, first') won't work.
Hates_ criteria answer didn't seem to work for me; putting "last,first" in order will only cause exceptions saying, "Property 'last,first' not found". To order on two fields, you can do the following:
def c = MyDomain.createCriteria()
def results = c.list {
and{
order('last','desc')
order('first','desc')
}
}
This is quite old but helped me in finding a suitable solution. A "cleaner" code example by using withCriteria shortcut:
def c = MyDomain.withCriteria {
and {
order('last', 'desc')
order('first', 'desc')
}
}
This old solution no longer works. Please see mattlary's answer below
You may have to write a custom finder in HQL or use the Criteria Builder.
MyDomain.find("from Domain as d order by last,first desc")
Or
def c = MyDomain.createCriteria()
def results = c.list {
order("last,first", "desc")
}
More complicated ordering criteria, (tested in Grails 2.1.0)
def c = MyDomain.withCriteria {
property {
order('last', 'desc')
}
order('first', 'desc')
}
sorts first by MyDomain.property.last then by MyDomain.first
MyDomain.findAll(sort: ['first': 'desc','last':'desc'])
works with grails-datastore-gorm:6.0.3
I think a criteria is the best bet, but you did the right thing by attempting a finder first. When retrieving domain objects from GORM, the right order to make the attempt is: dynamic finder, criteria, HQL.
This query is working on the basis of first field. When the first field is blank then it is shorted by the second field.
order('last','desc')
order('first','desc')
you can do this
def results=MyDomain.findAll([sort:"last",order:'desc'],[sort:"first",order:'desc']);
this line of code will first sort results from domain class "MyDomain" first by last name and then by first name of the person .
If you were sorting lists on the contents of their items, you would need to implement a comparator which would have some smarts to enable to you decide the sort order based on multiple properties.
Some examples of Groovy-style comparators are shown here
However if the list you are sorting is being returned from a database query, you would be better off sorting it using a CrteriaQuery and sorts on that
I has the same problem.
Since my list is not so big, I use groovy sort, as I want to sort on fields of linked domain:
CalendarData -> Attraction
def listCalendar (Calendar calendar) {
respond CalendarData.where {
calendar == calendar
}.list().sort{ "$it.attraction.type?:' '$it.attraction.name" }
}

Resources