Friday, November 10, 2017

Hibernate Proxies And Lazy Loading

Introduction: Database access is very costly for an application in the terms of performance. Hibernate uses lazy fetching plan by default to overcome unnecessary database hits i.e. hibernate by default loads only the object you are querying for.
For Example: If you have an entity Student, and you load the entity by using identifier like
          Student student = (Student)session.load(Student.class, 123);
This query will load only Student nothing else.
In this example  it looks Student object is available in persistence context but not actually. Hibernate creates proxy that looks like Student entity and place it in persistence context. Hibernate first checks, can it returns a proxy object for your query, if yes it returns and doesn’t hit database. Proxies: Proxies are place holders that are generated at runtime. Proxy is used to trigger the loading actual object when it is accessed very first time.
Student student = (Student)session.load(Student.class, 123);
         student.getId();
         student.getFirstName();
First two lines of this example don’t hit database. First line returns a proxy object, second line accessing identifier and database identifier property don’t need to initialize proxy. When you reach third line you are trying to access property of Student so proxy needs to initialize. It trigger SELECT query to database and fetch actual object and initialize Student proxy object with this object.
Some times we load an object from database for building some other complex object. Lets have an example of Blog application. A Comment object would need Blog and User objects.
         User user = (User)session.load(User.class, 234);
         Blog blog = (Blog)session.load(Blog.class, 1234);
         Comment comment = new Comment(“This is very informative Article”);
         comment.setUser(user);
         comment.setBlog(blog);
         session.save(comment);
Can you imagine no SELECT query has been fired in this example? Actually in both cases while we are loading User and Blog, hibernate returns proxy objects that we only need. So no need to hit database, it improves application performance.
This is the power of proxy. But be careful, proxy mechanism is not supported by get(). I mean if you are using get() method for retrieving objects, hibernate will not returns any proxy object. It will execute SELECT query and retrieve actual object(If it is not already in persistence context) and set into persistence context. This is the major difference between load() and get() methods. One more important difference between them is that load() throws an exception when entity not found in database while get() returns null in same case.
Proxies are not finished yet. Now supposed you have an object Comment in the memory which is either get explicitly or forced to initialize proxy by fetching its property. Now a fully loaded Comment object is in persistence context. Again you can feel the presence of proxies. No? Ok, let me explain, Your object can have *-to-one association, and these associated object are not loaded yet, proxies generated for them still have reference values only.
Hibernate also creates proxies for collections(hibernate have its own collections and it replaces your collections with them). Collections loaded or proxies initialized only when you starts iteration through them or perform some collection-management operation like size() or contains(). Hibernate provides alternative way to deal with large collections. Hibernate provide extra lazy option that can be set.
e.g.
<class name=”School” table=”SCHOOL”>
     …
     <set name =”students” lazy=”extra” inverse=”true”>
         <key column=”REGISTRATION_ID” />
         <one-to-many class=”STUDENT” />
     </set>
</class>
Now collections is no longer initialized if you call size(), isEmpty() and similar methods. For that database is queried to retrieved necessary information
Same optimization can be achieved using hibernate annotations.
@oneToMany
@org.hibernate.annotations.LazyCollection(
      org.hibernate.annotations.LazyCollectionOption.EXTRA
)
private Set<Student> students = new HashSet<Student>();

Disabling Proxies Generation: You are allowed to disable proxies generation and make fetch plan not lazy. Some times you need that your object should be always loaded. Then you need to disable lazy loading. You can disable lazy loading for specific class easily. i.g.
<class name=”Student” table=”STUDENT” lazy=”false”>

</class>
Using annotations:
@Entity
@Table(name=”STUDENT”)
@org.hibernate.annotations.Proxy(lazy=false)
public class Student(){…}

Now lazy loading is disabled for Student class.

0 comments:

Post a Comment