Why Hibernate Entity class should not be final in Java?

Hibernate extensively uses proxies. It uses proxies when we call load, it uses proxies for lazily loading associations and collections.But what if our entity class was final ?
                    One of the interesting hibernate interview questions is, why you should not make a Hibernate persistent class final? I'll try to answer this question in this short blog post. Use of proxies is the core feature of Hibernate (one of the most popular ORM framework for Java Projects) for implementing key performance features e.g. lazy loading and lazy associations fetching. In order to use a proxy in place of real class, your hibernate persistence class must be either non-final or the implementation of an interface that declares all of the public methods. Why? because you cannot extend a final class in Java, and to stand up as a proxy, proxy class must satisfy the IS-A relation, which comes either by extending a class using "extends", or implementing an interface using "implements".

By the way, it doesn't mean that you cannot persist your final entity class, you can, but this will limit Hibernate's ability to use proxies for lazy association fetching, which will affect the performance of Java application, read Java Persistence with Hibernate by Gavin King, the author of Hibernate to learn more about this feature.

Hibernate framework makes extensive use or proxy e.g. it uses proxies when you call the load() method, it also uses proxies for lazily loading associations and collections of an entity class. Even Hibernate  Reference Manual says "Prefer non-final classes".


Not using proxies is bad for performance because more SQL than you'd like may be fired against the database, which also increases database round-trips. You can verify this by printing class name of your entity class and look at Hibernate query logs,


Consider the simple class:

public class Project {
    private Long id;
    private String name;
    // setter getters
}
If I now run a simple load call:
public static void main(String[] args) {
    Session session = HibernateUtil.getSessionFactory().openSession();
    Transaction transaction = session.beginTransaction();
    Project  project = (Project) session.load(Project.class, 1L);
    System.out.println(project.getClass());
    transaction.commit();
    session.close();
}
The logs will indicate that no queries were fired.
class com.test.Project_$$_javassist_0
Also the class of the object is a JavaAssist generated proxy. I now set the class to final and executed the same code. The logs have now changed:
Hibernate: 
    select
        project0_.id as id1_0_,
        project0_.name as name1_0_ 
    from
        projects project0_ 
    where
        project0_.id=?
class com.test.Project
As can be seen final classes work perfectly with Java. However the ability to load the class lazily is lost. What if I made my getters final and left the class as non final ?
public class Project {
    private Long id;
    private String name;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(nullable = false)
    public final Long getId() {
        return this.id;
    }

    public final void setId(Long id) {
        this.id = id;
    }

   @Column(name = "name", length = 255, nullable = false)
    public final String getName() {
        return this.name;
    }


    public final void setName(String name) {
        this.name = name;
    }
If I run my main method the logs are :
class com.test.Project_$$_javassist_0
How did this work. I can understand my final setters working. But the final getters ?
You should also avoid declaring public final methods as this will again limit the ability to
generate proxies from this class. If you want to use a class with public final methods, you
must explicitly disable proxying.
This is from the hibernate docs.
I decided to modify the main method to display the project name:
public static void main(String[] args) {    
    Session session = HibernateUtil.getSessionFactory().openSession();
    Transaction transaction = session.beginTransaction();
    Project project = (Project) session.load(Project.class, 1L);
    System.out.println(project.getClass());
    System.out.println(project.getName());
    transaction.commit();
    session.close();
}
The output is :
class com.test.Project_$$_javassist_0
null

Important points to about Entity class, Final, and Proxying

Let's revise a couple of important things about a JPA entity class, final modifier and use of Proxy design pattern in Hibernate framework.

1) Hibernate doesn't create a proxy for final class, instead, they use the real class, but Hibernate does create a proxy for a non-final class with final methods.

2) Since in Java, you cannot override final methods, the code inside final method remain unchanged in the proxy class. Which means, if you call a final method on a proxy, it will simply delegate the call to the super class method. You should be careful not to modify state on final method because calling them on proxy has a good chance of throwing NullPointerException, as the object was likely to be not initialized at that point.

In short, avoid final methods on hibernate entity class, until you know what you are doing. Hibernate reference also stress this point "You should  avoid declaring public final methods as this will again limit the ability to generate proxies from this class. If you want to use a class with public final methods, you must explicitly disable proxying".

3) As per Hibernate documentation, if  you're going to make a class final you should explicitly disable proxy generation by adding @Proxy(lazy=false), but I haven't noticed any differences between doing that and just making the class final.

4) You should also disable proxy generation if you're going to make any public methods final in persistent class, as hibernate will not be able to override them to put its smart code which triggers lazy loading etc.

5) If you really want to make the Hibernate entity classes final then you can do it by having your entity implement an interface that declares all of its public methods. This will still allow Hibernate to use proxies.

In short, making a JPA Entity or Hibernate Persistence class final, limits the ability of Hibernate to use Proxies, which in turn prevent Hibernate from applying some performance optimizations. Without proxies, your application loses lazy loading, and lazy association fetching will issue more SQL queries and make more database roundtrip, which will cost performance.

The only way to make a Hibernate entity class final without losing lazy behavior is to use interface and define all public methods of persistence class there, this will still allow Hibernate to use a proxy in place of real class



No comments:

Post a Comment

Genuine websites to earn money.

If you are interested in PTC sites then this article is for you. I have personally tried many of the sites and found that the best thing ...