To work effectively with hibernate you need to understand how hibernate executes queries. You can get the work done by using various alternative APIs which hibernate provides, but selection of correct API is needed for performance optimization. In this post I will try to explain about how hibernate uses proxy design pattern to lazy load entities and how to effectively use it for performance optimization.
About Proxy Pattern:
Proxy pattern is used when you do not want to expose the actual object, the reason can be
- You want to hide the complexities of actual object. For example if the under laying object is handling data over network then you may want to create proxy object which will handle network related issues gracefully.
- Creation of actual object is expensive. Creation of actual object is postponed till it is really required; meanwhile the work can be done by simple proxy object.
How hibernate uses proxy pattern:
Hibernate uses proxy pattern to lazy load entities. For lazy loaded entity or collection, hibernate defers database hit till any property other than identifier (Property marked with @Id) is accessed. The proxy object which hibernate creates has only identifier value set. If it is collection then all item in collection has id set, no other properties are initialized.
Hibernate provides 2 different methods - get and load. Both methods use different fetching strategies. Load method uses lazy fetching whereas get method uses eager fetching.
So for example when you load the employee object in session like
Employee emp = session.load(Employee.class, 123L);
No database query is executed. Hibernate just create a placeholder object or proxy object which mocks Employee and returns that proxy object.
Now when you try to access the properties of Employee in persistent context like
emp.getName()
Then this is the first time hibernate will fire the select query which will load all properties of employee object. If above code is executed outside persistent context then Lazy initialization exception is thrown.
Consider the example of get
Employee emp = session.get(Employee.class, 123L);
This will immediately executes select statement and load all properties of employee object.
Behavior when employee with ID 123L exists in database.
Operation | Load | Get |
session.load/get | Returns proxy obhect(no select query is fired) | Select query is fired. Returns fully loaded object |
emp.getId() | No query is executed for identifier getter method provided you define method level annotation like @Id@GeneratedValue public Integer getId() { return id; } For Field level annotation it will fire select query. @Id@GeneratedValue private Integer id; |
Doesn't matter, as object is already loaded |
emp.getName() | Select query is fired. | As the object is already loaded, no database operation happens during this call. |
Behavior when employee with ID 123L doesn't exists in database.
Operation | Load | Get |
session.load/get | No Exception is thrown | null is returned |
emp.getName() | Exception is thrown (No row with the given identifier exists) |
How to improve performance using get and load.
Use case: Suppose you have department object and you just want to add employee to the department. So you will say
Employee emp = session.load(Employee.class, 1L); dept.add(emp); session.update(dept);
So if instead of load if you use get, then select query for employee will be fired which is really not required. In this way just by selecting load over get database hit can be reduced.
Conclusion: Using proxy object avoids unnecessary database hit. Use Load if you want to use proxy or use get if you don’t want proxy.
Note: Hibernate allows disabling proxy generation at class level by specifying @Proxy(lazy=false). If this annotation is used then no proxy is generated when loading the entity of this class. (In other words session.load(…) will also fire select query)
Collections which are initialized lazily also behave similar to the load method. For Lazy collection hibernate creates proxy which has only identifier values. This lazy collection is initialized as and when elements in collection are accessed based on fetch strategy (select fetch, subselect fetch, join fetch, batch fetch). Selecting appropriate fetch strategy also matters a lot from performance perspective. We will discuss this in next article.