[dropcap]R[/dropcap]ecently we have discussed about the performance of retrieving collections/relations/associations inside objects. By the way, we also review the machinery that hibernate handles these relational concepts to improve the effectiveness of database operations. One of the techniques being implemented in Hibernate is lazy loading. In this post, I want to share you what I have learned how hibernate lazy loading works with a toy application.
Table of Contents
Design the application
Here are our domain classes
package com.itersdesktop.javatechs.grails.hibernate class Book implements Serializable { String title static belongsTo = [author: Author] static constraints = { } }
package com.itersdesktop.javatechs.grails.hibernate class Author implements Serializable { String name static hasMany = [books: Book] static constraints = { } }
To test the application simply, we will try to create some toy records in bootstrap.groovy as below:
import com.itersdesktop.javatechs.grails.hibernate.Author import com.itersdesktop.javatechs.grails.hibernate.Book class BootStrap { def init = { servletContext -> def ethan = new Author(name: 'Ethan').save() def book1 = new Book(title: 'The Fairytale Hairdresser and Aladdin') def book2 = new Book(title: 'The How to be British Collection') ethan.addToBooks(book1) ethan.addToBooks(book2) } def destroy = { } }
In order to look at exactly when hibernate will invoke SQL queries, we can either turn on SQL logging in DataSource.groovy or enable the general query log. I suggest that we should use the former for simplicity.
... development { dataSource { dbCreate = "create-drop" // one of 'create', 'create-drop', 'update', 'validate', '' url = "jdbc:h2:mem:devDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE" 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):
package com.itersdesktop.javatechs.grails.hibernate class TestController { def index() { println "-- Starting controller" def ethan = Author.findByName('Ethan') println "author name is $ethan.name" def myBooks = ethan.books println "books are now stored in a variable" myBooks.each({ println it.title }) println myBooks.class.name render "Finish!" } }
The method just retrieves the author named ‘Ethan’ that has been created in BootStrap, then collect all his books and print out their name.
Have you ever wondered that how many query commands could hibernate issue from just the simple method index of TestController? It might be called at the line 6, 8 or 10. Now we are going to go through the code and analyse phenomenon could be occurred.
Forecast likely circumstances
At the line 6, we are using a dynamic finder to look up author based its name. Hibernate is likely to issue a query to get the instance of Author class at which name is ‘Ethan’.
[box type=”note” ]Dynamic method that uses the properties of the domain class to create query method expressions that return all matching instances of the domain class.
See more at: http://docs.grails.org/2.5.5/ref/Domain%20Classes/findAll.html[/box]
At the line 8, we are asking for the books field where hibernate will retrieve them. Unfortunately Grails uses lazy loading by default, so we cannot see any queries to be issued from this call. Indeed, books haven’t been touched at this point. To ensure the performance, hibernate lets them lived as soon as there are another actually operations hitting the database.
Hibernate won’t actually hit the database to retrieve the data until the line 10 because we expect to get individual book via iterating on the books collection of the given author.
The line 13 reveals how the magic happens. We expect myBooks to be an instance of List , but it is literally one of PersistentSet.
Analyse SQL queries log
--- Starting controller Hibernate: select this_.id as id1_0_0_, this_.version as version2_0_0_, this_.name as name3_0_0_ from author this_ where this_.name=? limit ? author name is Ethan books are now stored in a variable Hibernate: select books0_.author_id as author_i3_0_0_, books0_.id as id1_1_0_, books0_.id as id1_1_1_, books0_.version as version2_1_1_, books0_.author_id as author_i3_1_1_, books0_.title as title4_1_1_ from book books0_ where books0_.author_id=? The Fairytale Hairdresser and Aladdin The How to be British Collection org.hibernate.collection.internal.PersistentSet
The output above proofs what I have explained you in the previous sentences.
This writing was given birth to based on what I had read from [1] and various experiences on real projects I am working on. Hopefully you find more interesting as well as helpful from this post. It’s more respectful to have your comments and suggestions in the comment sections.
Source codes
The toy application can be found at https://bitbucket.org/it4bus/hibernate-lazy-evaluation.
References
[1] Hibernate magic with lazy evaluation, accessed 25th February, 2017