Hibernate Best Practices Tutorial

Hibernate is the popular object relation mapping implementation. This feature makes it special among the developers and in this tutorial we will see the best practices to create better Hibernate applications.


1. Hibernate Introduction

  • Object-Relational Mapping or ORM is the programming technique to map application domain model objects to the relational database tables
  • Hibernate is a Java-based ORM tool that provides a framework for mapping application domain objects to the relational database tables and vice versa. It provides a reference implementation of the Java Persistence API that makes it a great choice as an ORM tool with benefits of loose coupling
  • A framework that provides the option to map plain old Java objects to traditional database tables with the use of JPA annotations as well as XML based configuration
  • A framework that provides the data query and retrieval facilities and is purely used for the data persistence (i.e. to store/retrieve data from the database)
  • A framework that internally uses the JDBC API to interact with the database. It hides the internal JDBC implementations from the end users

Hibernate Best Practices - Hibernate Overview
Fig. 1: Hibernate Overview

1.1 Hibernate Architecture

There are 4 layers in the Hibernate architecture i.e. Java Application Layer, Hibernate Framework Layer, Backend API Layer, and the Database Layer. Let’s understand the diagram of Hibernate architecture.

Hibernate Best Practices - Architectural Diagram
Fig. 2: Hibernate Architectural Diagram

For creating the first Hibernate application, we must know the elements of the Hibernate architecture. They are as follows:

SessionFactoryThe SessionFactory is a factory of session and client of Connection Provider. It holds second level cache (optional) of data.
SessionThe session object provides an interface between the application and data stored in the database. It is a short-lived object and wraps the JDBC connection. It is a factory of Transaction, Query and Criteria and holds the first-level cache of data. The Session interface provides methods to INSERT, UPDATE, and DELETE the objects.
TransactionThe transaction object specifies the atomic unit of work and is an optional parameter in the Hibernate framework.
ConnectionProviderIt is a factory of JDBC connections and abstracts the application from DriverManager or DataSource. It is an optional parameter in the Hibernate framework.
TransactionFactoryIt is a factory of Transaction and is again an optional parameter in the Hibernate framework.

1.2 Hibernate Benefits

There are many advantages of using the Hibernate framework, e.g.

  • Hibernate framework is open source and lightweight
  • The performance of the Hibernate framework is fast and supports smart fetching techniques because of the internal caching mechanism
  • Hibernate framework provides the facility to create the database tables automatically
  • With the help of HQL (Hibernate Query Language), the generated SQL queries are independent of databases
  • Provides query facilities to fetch the data from multiple databases, and supports transaction management and automatic key generations
  • Provides APIs for storing and retrieving the Java objects directly to and from the database
  • The framework takes care of the mapping Java classes to database tables using XML files or annotations

2. Hibernate Best Practices

Let us explore the different Hibernate strategies that can be adopted to improve the performance of an application.

2.1 Use of Model classes

While writing SQL Select query, developers can choose the columns they need for implementation. JPA and Hibernate support specific columns than just entities. There are of 3 types and each has its own usage.

2.1.1 Entity

An entity is the most common implementation. Developers can use it if they need all entity attributes or to perform the SQL Update or Delete operations that affect a minimal entity number.

em.find(Person.class, 4);

2.1.2 Model class a.k.a POJO

The POJO is similar to Entity class but it represents a specific record in the database.

List list= em.createQuery(“SELECT new Bookdetails(book.isbn, book.author) FROM Bookdetails book”, Bookdetails.class).getResultList();

2.2 Using the Query

ORM frameworks offer multiple options to create an SQL query that matches their requirement. Let us understand them one by one.

2.2.1 find() method

This method is the easiest implements to find a record from the database by its primary key. This method not only provides security and performance benefits. It is also:

  • Checking the record in the 1st and 2nd level cache to save the costly trips to the database
  • Avoiding the SQL injection problems
em.find(Person.class, 5);

2.2.2 Java Persistence Query Language (JPQL)

The Java Persistence Query Language (JPQL) is similar to SQL queries but it executes on entity class and their relations but not directly on database tables. This approach offers low and moderate complexity.

TypedQuery tq = em.createQuery(“SELECT book FROM Book book JOIN book.author WHERE book.title = :title”, Book.class);

2.2.3 Criteria API

The Hibernate’s Criteria API generates dynamic queries at runtime. Developers can use this if the query structure depends on user input. Let us understand this with the help of an example.

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery q = cb.createQuery(Book.class);
Root book = q.from(Book.class);
if (!input.getTitle().isEmpty()) { 
SetJoin join= book.join(Book.class); 
q.where(cb.equal(join.get(Booktitle.class), input.getTitle()));

2.2.4 Native SQL Queries

Native Queries provide developers the way to write and execute the database statements. This is the best way to write complex queries in the Hibernate framework.

Myclass e = (Myclass) em.createNativeQuery(“SELECT * FROM myClass e WHERE e.name =“abc“, Myclass.class).getSingleResult();

2.3 Use Bind Parameters

Using parameter bindings for the query offers several advantages over the regular SQL query strings.

  • No SQL injection
  • Automatic mapping of query parameters to its correct type
  • Increased performance

These are represented by a number starting with 1 and prefixed with ?.

Query q = em.createNativeQuery(“SELECT c.firstname, c.lastname FROM Employee c WHERE c.id = ?”);q.setParameter(1, 1);

2.4 Do not use Eager Loading

Eager loading the records from the database is another reason that affects Hibernate performance.

@ManyToMany(mappedBy = “authors”, fetch = FetchType.EAGER)
private Set books = new HashSet();

The framework fetches the related entities from the database based on the relationship and the defined fetch mode. This result in confusion as hibernate fetches the related entities data which may be required from the given test case. To overcome this issue developers should use the fetching mode as FetchType.LAZY.

2.5 JDBC Batching

Jdbc allows batching the multiple SQL statements and sending them to the database in a single request. This approach saves multiple trips for all the SQL operations and reduces the response time.

2.6 Automatic Primary Key Generator

Hibernate use the existing database features to auto-generate the unique id identifier otherwise called as Primary key column values. The following code snippet will help us understand the use of @Id annotation.

@Id@GeneratedValue@Column(name = “id”, updatable = false, nullable = false)
private Long id;

3. Summary

Here are some points that can help us while using the Hibernate Framework:

  • Prefer using session.get() instead of session.load(). Load always returns the proxy to avoid getting LazyInitializationException
  • Always set lazy=true for collection mappings and use Join Fetch in HQL or setFetchMode() method in Criteria API to retrieve collections
  • Use surrogate id in the data model instead of Composite Keys and override equals and hashCode method using the business key to identify uniqueness
  • As HibernateException is RuntimeException never catch them at the business layer and have them be propagated to UI Layer
  • Use SQL Query cache for read-only data
  • Many-One Mapping should preferably have lazy=false and One-Many should have lazy=true. To avoid N+1 Query problem in Hibernate use Eager Fetching technique or Batch settings
  • Don’t retrieve too much data in one SQL query and use Paging, Fetch Strategy, and carefully use the SQL Join to get the needed data
  • Use 2nd Level Caching technique for read-only data
  • Do not perform bulk operations with Hibernate

That’s all for this post. Happy Learning!!

4. Conclusion

Following is an extract of good practices in Hibernate. Developers can use these tips to implement in their application and offer better performance. I hope this article served you whatever you were looking for.


An experience full-stack engineer well versed with Core Java, Spring/Springboot, MVC, Security, AOP, Frontend (Angular & React), and cloud technologies (such as AWS, GCP, Jenkins, Docker, K8).
Notify of

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Inline Feedbacks
View all comments
Back to top button