Most programmers come into contact with Hibernate proxies when they face the ominous LazyInitializationException. The bad taste is hard to wash away, but Proxies are a necessary “evil” when working with Hibernate. This article will dig into why proxies are important. It will also point out some pitfalls that occur often when working with proxies.
What are proxies?
Proxies are the mechanism that allows Hibernate to break up the interconnected cloud of objects in the database into smaller chunks, that can easily fit in memory. Let’s look at an example.
PERSON | ||
---|---|---|
ID | NAME | BOSS |
1 | Daan | NULL |
2 | Vincent | 1 |
3 | Eelco | 1 |
4 | Jan | 2 |
5 | Maarten | 3 |
6 | Erik | 3 |
public class Person { private Long id; private String name; private Person boss; private Set employees; }
This is a very simple example. The class on the right is mapped to the table on the left. So what happens if Hibernate loads the row with NAME=’Maarten’? To store the data in memory, Hibernate will create a new Person object and set the columns to the fields. It stores the ID column in the id field and the NAME column in the name field. Coming to the BOSS column, Hibernate faces a problem: The type of the column is NUMERIC, but the type of the property is Person. This is of course a foreign key, to another row in the PERSON table. Now, Hibernate could simply load the data from the associated row and create a new Person object and store it in the field, but this would cascade eventually to all rows in the table (and possibly other tables in the database), especially taking into account the employees property as well.


Proxy Pitfall 1: Field Access
Coming to the first pitfall with proxies: When a proxy is loaded, the data is not stored in thr proxy object, but in the "target" object. The fields in the proxy object will remain null forever (or any value that they have been initialized with), since all methods invoked on the proxy will be delegated to the target object. The main area where this Pitfall arises is the equals(..) method. A very common implementation would be something like this:@Override public boolean equals(Object obj) { if (obj == null) { return false; } if (obj == this) { return true; } if (!(obj instanceof Person)) { return false; } return name.equals((Person)obj).name); }checking the name field of the two Person objects to be the same. This will fail however when we invoke it like personObject.equals(personProxy). In the last line of the method we use direct field access on the other object to look at the name field. Since the other object is a proxy, the field will always be null, even if the proxy is in fact encapsulating the personObject itself. Although the equals(..) method is a typical problem area for this pitfall, it is definitely not limited to that. Every method that takes the declaring class as parameter (and thus allows access to fields on that parameter) has the same problem. To prevent this, use getters and setters in these occasions. The proxy will be loaded when the getter is invoked and the data will be accessible.
Proxy Pitfall 2: instanceOf

Conclusion
Proxying is one of the most important features of Hibernate. Although it takes some time to see its importance and to work with its features, doing so will help you develop applications with Hibernate more easily. Keeping in mind the two pitfalls that are discussed in this blog will prevent hard to find bugs in your applications.Maarten Winkels
Contact