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