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.
Table of Contents
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 internalJDBC
implementations from the end users
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.
For creating the first Hibernate application, we must know the elements of the Hibernate architecture. They are as follows:
Element | Description |
---|---|
SessionFactory | The SessionFactory is a factory of session and client of Connection Provider. It holds second level cache (optional) of data. |
Session | The 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. |
Transaction | The transaction object specifies the atomic unit of work and is an optional parameter in the Hibernate framework. |
ConnectionProvider | It is a factory of JDBC connections and abstracts the application from DriverManager or DataSource . It is an optional parameter in the Hibernate framework. |
TransactionFactory | It 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 generatedSQL
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.
1 | 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.
1 | 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
1 | 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.
1 | 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.
01 02 03 04 05 06 07 08 09 10 | CriteriaBuilder cb = em.getCriteriaBuilder(); CriteriaQuery q = cb.createQuery(Book. class ); Root book = q.from(Book. class ); q.select(book); if (!input.getTitle().isEmpty()) { SetJoin join= book.join(Book. class ); q.where(cb.equal(join.get(Booktitle. class ), input.getTitle())); } </book,> |
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.
1 | 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 ?
.
1 | 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.
1 2 | @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.
1 2 | @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 ofsession.load()
. Load always returns the proxy to avoid gettingLazyInitializationException
- Always set
lazy=true
for collection mappings and useJoin Fetch
inHQL
orsetFetchMode()
method inCriteria
API to retrieve collections - Use surrogate id in the data model instead of Composite Keys and override
equals
andhashCode
method using the business key to identify uniqueness - As
HibernateException
isRuntimeException
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 havelazy=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 theSQL
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.