Friday, July 23, 2021

Why JPA Entity or Hibernate Persistence Class Should Not be Final? Answer

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. The use of proxies is the core feature of Hibernate (one of the most popular ORM frameworks 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 a 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, the 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 of 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".

And, if you are serious about improving your Hibernate and JPA skills then I also recommend you check out these Hibernate and JPA courses from Udemy and Pluralsight. It's a great course to learn Hibernate and JPA with Spring Boot in a hands-on and practical manner. 



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


Let's say you load your entity class like a Book, an Employee, or a Customer using the load() method and further print the name of the class, you will see a different name e.g.

Customer customer = (Customer) session.load(Customer.class, 1L);
logger.info("Name of Customer Entity class : " + customer.getClass());

will print "sample.hibernate.Customer_$$_javassist_0" which is a JavaAssist generated proxy.


You will not see any select query fired because the load is lazy and it will not load objects until you call any method other than the getId(), as seen in the difference between the get() vs load() in Hibernate article.

Now, if you make the class final and reprint the name of the class, you will see the actual name of the class as "Customer". You will also see the select queries fired by hibernate to initialize the object.

You should now understand how costly it can be if you are creating too many objects and only accessing their getId() method, as it will result in lots of database calls. That's why you should not make your JPA Entity or Hibernate persistence class final in Java. Let's see some more points in the next section.

Hibernate interview questions with answers




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 the Hibernate framework.

1) Hibernate doesn't create a proxy for the 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 the final method remains unchanged in the proxy class. This means, if you call a final method on a proxy, it will simply delegate the call to the superclass method. You should be careful not to modify the state on the 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 stresses 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 prevents 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 an interface and define all public methods of persistence class there, this will still allow Hibernate to use a proxy in place of real class.


Related Hibernate and Spring Interview Questions
  • Difference between save(), persist() and saveOrUpdate() in Hibernate? (answer)
  • Difference between First and Second level cache in Hibernate? (answer)
  • Difference between get() and load() method in Hibernate? (answer)
  • What should you remember while overriding equals() and hashCode for Hibernate entity class?(answer)
  • Why default or no-argument constructor is important in Java? (answer)
  • When should you use setter and constructor injection in Spring? (answer)
  • What is the default bean scope in the Spring framework? (answer)
  • What is the difference between BeanFactory and ApplicationContext in Spring? (answer)

P. S.
- If you are serious about improving your Hibernate and JPA skills then I also recommend you check out these best Spring and Hibernate courses. It's a great course to learn Hibernate and JPA with Spring Boot in a hands-on and practical manner. It's also very affordable and you can buy it for just $10 on Udemy flash sales.


No comments :

Post a Comment