How to limit the Relation Elements of a Class in Grails? - grails

I have
class User {
String name
hasMany = [books: Book]
}
class Book (
String name
belongsTo = [user: User]
}
Now, I can access all books of a user instance as:
def user = User.find("someId")
println user.books
How can I limit the number of books such that I get only the first x books from user.books?
Is there also a way to sort them?

One viable approach is to define books as List inside User. You would need to have an index column but you could get the benefit of pagination and ordering like:
class User{
List books
static hasMany = [books: Book]
}
def user = User.find("someId")
println user.books?.getAt(3..10).sort{it.name}
Note:-
All books for User will be fetched lazily, using the above approach will filter books from index 3 till 10.
In case you want to optimize the lazy fetch strategy (N + 1), then you would probably need to have a look at batchSize and order. Also sort as a side note.
Example:
class User {
String name
static hasMany = [books: Book]
static mapping = {
books batchSize: 10
}
}
class Book (
String name
static belongsTo = [user: User]
static mapping = {
order "desc"
}
}

Related

Grails domain self-reference recursive call

Here is my domain class:
class Category {
String name
static hasMany = [subCategories: Category]
static belongsTo = [parentCategory: Category]
static mapping = {
subCategories joinTable: false, column: "parent_id"
}
static constraints = {
parentCategory nullable: true
}}
Category is a domain class and has self-reference to both parent and list of children.
Now I want something like this: given a parent category id, I want a list of all sub-categories belong to this id. (NB: not direct children, all the children under the id)
For example, id 1 has children 2 and 3, and 2 has children 4 and 5.
Given I got category id 1 from client, I want a sub categories with id 2,3,4,5
Taken advantage of Groovy, what is the best code to implement that?
Untested code but might get you moving in the right direction. There might be a "groovier" way to do this, but I'm not sure.
def findAllChildren(category, results = []) {
category.subCategories.each { child ->
results << child
findAllChildren(child, results)
}
}
def someOtherMethod() {
def allChildren = []
findAllChildren(parentCategory, allChildren)
}

Criteria search

I ran into some problems while trying to count items.
Imagine the following domain classes
class Book {
String name
}
class Author {
String name
static hasMany = [books:Book]
}
How do I get a list of Authors sorted by number of Books?
here's my try:
def c = Author.createCriteris()
c.list {
projections {
count 'books', 'numBooks'
groupProperty 'id'
}
order 'numBooks', 'desc'
}
but somehow I get only unusable results... and I don't know how to join the Author objects to the rsult list.... :-(
Havent tried it, but couldn't you do something like:
class Author {
String name
static hasMany = [books:Book]
static namedQueries = {
sortByMostBooks {
books {
order('size', 'desc')
}
}
}
}
And then get access by the cleaner named query
Author.sortByMostBooks.list();
In addition, you may want to include a belongsTo in you Book domain class:
static belongsTo = Author;
or:
static belongsTo = [authors:Author];
if a book is likely to have multiple authors
got something!
I still don't know how to do it with a criteria, but by switching to HQL, I succeeded.
So if someone comes up with a criteria solution, he will still get the bonus for the correct answer :-)
here is my query:
Author.executeQuery("""
select
a, size(a.books) as numBooks
from
Author a
group by
id
order by
numBooks DESC
""",[max:20])
This query isn't efficient, since it fetches all Authors in a loop, but that's ok for now.

Grails: How to query objects in many to many mapping?

Hello I have the follwing domain classes.
class Student {
int age
static hasMany = [courses:Course]
}
class Course {
String name
static hasMany = [students:Student]
}
I want to find the Students taking Course (with id 1), with age 7.
Could I do that with dynamic finder or criteria builder or HQL?
I do not want to do following as it load all students so inefficient:
def course = Course.get(1);
course.students.findAll{ it.age == 7 }
def studs = Student.withCriteria {
eq('age', 7)
courses {
eq('id', 1)
}
}
It's in GORM doc, section "Criteria/Querying Associations".
You can use a dynamic finder:
def students = Student.findByCourseAndAge(Course.load(1), 7)
By using load() instead of get() you avoid retrieving the whole Course instance and just reference its id.
Another option is HQL:
def students = Student.executeQuery(
'from Student s where s.course.id = :courseId and s.age = :age',
[courseId: 1, age: 7])

Grails/GORM saves in the wrong order

I am using Grails 1.3.6 and I am having problems getting saves to cascade properly. The problem is specifically with classes that have two parent classes. A simplified version of what I am attempting is this:
class Location {
String city
static hasMany = [authors: Author, publishers: Publisher]
}
class Author {
String name
static belongsTo = [location: Location]
static hasMany = [books: Book]
}
class Publisher {
String name
static belongsTo = [location: Location]
static hasMany = [books: Book]
}
class Book {
String title
static belongsTo = [author: Author, publisher: Publisher]
}
class Srv1Service {
static transactional = true
def loadData() {
def l1 = new Location(city: "London")
def a1 = new Author(name: "Graham Greene")
l1.addToAuthors(a1)
def p1 = new Publisher(name: "Some Press")
l1.addToPublishers(p1)
def b1 = new Book(title: "The Comedians")
a1.addToBooks(b1)
p1.addToBooks(b1)
l1.save()
}
}
If I run the above loadData, the Book instance is saved before the Publisher instance, resulting in the error "not-null property references a null or transient value: adhoc.Book.publisher".
I have tried various different ways of defining the relationships with little success. I have tried interim saves, and this does work, but I can see that parent tables are updated as I save the child data - ie Location, Author and Publisher all get updated to version 1. (And also I would like to keep the code as simple as I can.) I would like to avoid linking tables.
Any advice is gratefully received!
Okay, so the key here is that saves are cascaded from parent to children. You have a problem when it comes to Book because Book is the child to both Publisher and Author. GORM tries to save Location, Location tries to save Author, Author tries to save Book BUT the save fails because Book has a transient Publisher.
Try adding an intermediate save right before you create your Book:
def loadData() {
def l1 = new Location(city: "London")
def a1 = new Author(name: "Graham Greene")
l1.addToAuthors(a1)
def p1 = new Publisher(name: "Some Press")
l1.addToPublishers(p1)
l1.save() // add this save
def b1 = new Book(title: "The Comedians")
a1.addToBooks(b1)
p1.addToBooks(b1)
l1.save()
}
I created a local Grails project with your domain classes adding in this save. The cascading is working as you would expect.

Grails GORM Domain class relationship

Grails 1.1.1
Goovy 1.5.7
In a relationship such this:
Author 1 -- n Book n -- 1 Publisher
Defined in Grails:
class Author {
String firstName
String lastName
static hasMany = [books: Book]
static constraints = {
books(nullable: true)
}
}
class Book {
String title
Author author
Publisher publisher
static constraints = {
author(nullable: true)
publisher(nullable: true)
}
}
class Publisher {
String name
static hasMany = [books: Book]
static constraints = {
books(nullable: true)
}
}
I want to load a Book with the values of Publisher and Author.
When i get a Book with the query:
def book2 = Book.findAllByAuthor(author)
I get the response with the autor assosiated but the publisher only have the id and name class in the other query:
def book3 = Book.findAllByPublisher(publisher)
I retrieve me the inverse result,i have the book with the publisher data but the author only have the id and the class name.
Where is the error in the defined model ? o there is an error in the way to do the queries ?
Edit:
I need the way to retrieve the values only with the query like this:
def book2 = Book.findAllByAuthor(author, [fetch:[publisher:'eager']])
In this one I can manage the value of publisher.
Question: If publisher had a hasmany or Domain related, getting the book I'm able to read the attributes?
Thanks.
Thanks.
Lazy fetching is used by default with gorm associations. If you want to enable eager fetching, you can modify the ORM DSL by adding the following mappings block to your Author domain class:
static mapping = {
books lazy:false
}
or you could change the fetch mode in the domain object by adding following code after your books relationship is defined.
static fetchMode = [books:"eager"]
Doing the same to your Publisher domain object should allow you to accomplish what you want. You do want to be careful of the consequence that you may load more data than you intend to.
Shouldn't the get() method return what you are looking for?
Example: def book2 = Book.get(author)
You'd better use Criteria and explicitly define which relations should be loaded eagerly. Just mention relation in the query.
Example:
def c = Teacher.createCriteria()
List<Teacher> results = c.list {
subjects {
attendees {}
}
}

Resources