In a database, that gathers authors and their books, we add the books by an Author
as Book
instances.
However, an Author
may have books that have not yet been added to the database. This is why we have an outside sanity counter, telling us how many books an Author
actually has published. If the numberOfKnownBooks
is equal to the number of Book
instances in the database, then we consider that we have an Author
's complete bibliography.
An SQL query to obtain all the Authors
whose bibliography is complete would be written like this in SQL:
select author.id, count(*)
from book inner join author on book.author_id=author.id
group by author.number_Of_Known_Books, author.id
having count(*) = author.number_Of_Known_Books
The difficulty here is that the join points in the opposite direction, and that normal examples usually show how to create a query to obtain Books
joined with Authors
, not the other way around.
I have found some articles in stackoverflow explaining how to do something like this in plain JPQL (minus the group by ... having complication) here, but doing this in the DSL expected by the @Query
annotation in a Data Service in GORM 6.1+, I get an exception, occurring in the dungeons of generated code by an AST, saying that it can't find method executeQuery
on java.lang.Object
. This is a reasonable complaint, but I have no idea why it is happening. No way to step debug this code unfortunately.
This is my Data Service:
import grails.gorm.services.Query
import grails.gorm.services.Service
@Service
abstract class AuthorService {
@Query("""
select a.*, count(a.id)
from ${Book b} join ${b.author} a
group by a.numberOfKnownBooks, a.id
having count(*) = a.numberOfKnownBooks
""")
abstract List<Author> retrieveCompleteAuthors()
}
UPDATE: removed useless annotations @GrailsCompileStatic and @Transactional as per jeff's instructions
FULL SOLUTION, with the right JPQL query:
@Service(Author)
abstract class AuthorService {
@Query("""
select auth from ${Author auth} where auth.id in (
select a.id
from ${Book b} join ${b.author} a
group by a.numberOfKnownBooks, a.id
having count(*) = a.numberOfKnownBooks
)
""")
abstract List<Author> retrieveCompleteAuthors()
}
I expect that isn't really the HQL you want but that is a separate issue. The error you asked about should go away if you do something like this...
import grails.gorm.services.Query
import grails.gorm.services.Service
@Service(Author)
abstract class AuthorService {
@Query('''
select a.*, count(a.id)
from ${Book b} join ${b.author} a
group by a.numberOfKnownBooks, a.id
having count(*) = a.numberOfKnownBooks
''')
abstract List<Author> retrieveCompleteAuthors()
}