I’ve just run across this phenomenon for the first time and it turns out that hibernate can be even lazier than I thought! To illustrate lets use this time-honoured example in Grails.

Here are our domain objects:

class Book {  
    String title  
    static belongsTo = [author : Author]  
    static constraints = {  
    }  
}  
class Author {  
    String name  
    static hasMany = [books : Book]  
    static constraints = {  
    }  
}

and we’ll add some sample data in our bootstrap.groovy:

def init = { servletContext ->  
    def alice = new Author(name: 'alice').save()  
    def book1 = new Book(title: 'book1')  
    def book2 = new Book(title: 'book2')  
    alice.addToBooks(book1)  
    alice.addToBooks(book2)  
}

Now we’ll turn on SQL logging in Datasource.groovy, so we can see exactly when hibernate is issuing SQL queries:

dataSource {  
    dbCreate = "create-drop" // one of 'create', 'create-drop','update'  
    url = "jdbc:hsqldb:mem:devDB"  
    loggingSql = true  
}

Now let’s create a controller to hold our test code (just for convenience, this could also be done in a groovy console):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class TestController {

    def index = {

        println "starting controller"  
        def alice = Author.findByName('alice')  
        println "author name is $alice.name"  
        def myBooks = alice.books  
        println "books are now stored in a variable"  
        myBooks.each({println it.title})  
        println myBooks.class.name  
        render "OK!"  
    }  
}

This is pretty straightforward – we’re using a dynamic finder to retrieve the Author we added in the bootstrap script, then getting the list of Books and storing them in a variable, then printing the title of each book.

Question: when do we expect to see the SQL query issued to retrieve the books? Grails uses lazy evaluation by default, so it won’t be at line 6, because we haven’t actually asked for the books field yet. I expected to see it at line 8 – here we are asking for the books field, so surely this is where hibernate will retrieve them. I was wrong – in fact the books are not retrieved until line 10, where we actually want to do something with the books. The output make this clear:

starting controller  
Hibernate:  
select  
this_.id as id0_0_,  
this_.version as version0_0_,  
this_.name as name0_0_  
from  
author this_  
where  
this_.name=?  
author name is alice  
books are now stored in a variable  
Hibernate:  
select  
books0_.author_id as author3_1_,  
books0_.id as id1_,  
books0_.id as id1_0_,  
books0_.version as version1_0_,  
books0_.author_id as author3_1_0_,  
books0_.title as title1_0_  
from  
book books0_  
where  
books0_.author_id=?  
book2  
book1  
org.hibernate.collection.PersistentSet  

So hibernate does not actually execute the SQL query to get the books when we ask for alice.books, but it is lazy enough to wait until we actually we try to iterate over the results. The last line of the output reveals how the magic happens – the object that myBooks points to is not a straightforward List, but a PersistentSet.

This caused me a fair amount of confusion when I was trying to optimise a Grails action by minimising the number of SQL queries issued when retrieving a large number of objects from a database. Now that I know the way that it really works, I find it both cool, and a little bit scary that this is going on behind the scenes. A nice illustration of the trade-offs involved in using an ORM framework.


Subscribe to articles from the programming category via RSS or ATOM

Comments

comments powered by Disqus