Criteria on domain which does not have reference of another domain - grails

I have domain like
class Book {
Author author
String title
}
and another
class ListingTracking {
Book book
String oldVal
String newVal
Date modDate
static constraints = {
}
}
and now I need all those listingTacking which have a list of book ids but my criteria is on Book domain and I can not change association.
I tried
def books= Book.createCriteria().list {
'in'("id", [7,2,3,6].collect {it as Long} )
createAlias("listingTracking","lt")
projections{
property("title")
}
'in'("lt.book.id",[7,2,3,6].collect {it as Long})
}
I know that I can't create createAlias of listingTracking in Book domain but I need something like that, Is this possible via criteria?.

def listingTracking = ListingTracking.findAllByBookInList( Book.getAll([7,2,3,6]) )
Instead of Book.getAll you can use any finder you prefer on Books

Related

Duplicates to be allowed in the mapping table for many to many relationship with extra column

I have two domain classes with many to many relationship with extra column.I created below domain classes by following the logic from the forums and still face an issue in saving the data in additional domain class.Roylaty is the additional column to save the value in the mapping table.
Below are the 3 domain classes:
class AuthorBook implements Serializable {
Author author
Book book
String royalty
boolean equals(other) {
if (!(other instanceof AuthorBook)) {
return false
}
other.author?.id == author?.id &&
other.book?.id == book?.id
}
int hashCode() {
def builder = new HashCodeBuilder()
if (author) builder.append(author.id)
if (book) builder.append(book.id)
builder.toHashCode()
}
static AuthorBook get(long authorId, long bookId) {
find 'from AuthorBook where author.id=:authorId and book.id=:bookId',
[authorId: authorId, bookId: bookId]
}
static AuthorBook create(Author author, Book book, boolean flush = false) {
new AuthorBook(author: author, book: book).save(flush: flush, insert: true)
}
}
class Author implements Serializable{
string name(nullable:false,unique:true)
Set<Book> getBooks() {
AuthorBook.findAllByAuthor(this).collect { it.book } as Set
}
}
class Book implements Serializable{
string title(nullable:false,unique:true)
Set<Author> getAuthors() {
AuthorBook.findAllByBook(this).collect { it.author } as Set
}
}
In one of my controllers i wrote the below logic:
def author1 = new Author("ABC")
author.save(flush:true)
def book1= new Book("GORM")
book.save(flush:true)
def authorBook = new AuthorBook(royalty:100,author:author1,book:book1)
authorBook.save(flush:true)
For both author and book, it works as expected i.e it won't allow duplicates and in the mapping table too. it won't allow duplicates but I want the output to be as below in the mapping table
Author AuthorBook Book
id Name id author_id book_id royalty id title
1 XYZ 1 1 1 500 1 Gorm
2 1 1 1000
It won't save this value as it is considering the combination of author_id and book_id to be unique even though I did not set any composite key on id's in the mapping table.
What should I change in the mapping table to allow duplicates?
Can you manually insert that row into the database? I suspect this is caused by your implementation of equals and hashcode on AuthorBook.
These two objects are the same:
author=1;book=1;royalty=100 and author=1;book=1;royalty=500 because your equality methods are only comparing author and book.

GORM or HSQL for left join exclusive

I have Course domain,
Course has one teacher or null
I want to find all courses which either has no teacher or teacher.id != :loginId
How can I write query using GORM dynamic find* methods
Or write it using HSQL
- My teacher property is User domain
Appreciate your help
hasOne Class Structure
class Course {
User teacher
static hasOne = [
teacher: User
]
}
class User {
// implicit id field
}
Using HQL
def getCourses(def loginId) {
return Course.executeQuery("""
SELECT
c
FROM
Course c
LEFT OUTER JOIN c.teacher as t
WHERE
(t.id = NULL OR t.id != :logIn)
""", [loginId: loginId])
}
Using CriteriaBuilder
import org.hibernate.criterion.CriteriaSpecification
def getCourses(def loginId) {
return Course.createCriteria().list{
createAlias(
"teacher",
"t",
CriteriaSpecification.LEFT_JOIN
)
or {
ne("t.id", loginId)
isNull('t.id')
}
}
}
I'm going off of past experiences so I haven't tested your exact scenario, but I believe both options should work. I'm under the impression that a grails dynamic finder would not work in this case because of the nested condition you need (course.teacher.id != loginId).

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 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