In this article, I will try to help you navigate the maze of possibilities offered by Hibernate. I will explain where and how to use the title SessionFactory, EntityManager, and JpaRepository/CrudRepository. People learning Hibernate often come across a sea of possibilities while searching for solutions on the Internet.

Preface

When starting their adventure with Hibernate, many people get lost in the sea of possibilities that Hibernate offers. Let me start by saying that Hibernate is one of the JPA – Java/Jakarta Persistence API implementations. Popular JPA implementations: Hibernate, EclipseLink, OpenJPA and DataNucleus.

Learning Hibernate - like most frameworks - is worth starting with official documentation. Already on the home page we see a very telling banner "More than an ORM, discover the Hibernate galaxy." Hibernate includes, for example: Hibernate Search and Hibernate Validator (used in the Spring Framework - Spring MVC).

Ways to work with Hibernate – 4 different ones

From the official documentation, specifically from the part "Getting Started with Hibernate“, we learn that Hibernate can be used in three ways , I'll add the fourth one.

Getting Started with Hibernate:

  1. Using native Hibernate APIs.
  2. Using JPA-standard APIs.
  3. Using Envers.
  4. (I add) Using Spring Data JPA.

I will describe each of the above points in detail later.

Explanation of basic elements

Before we dive into how to use Hibernate, let's explain some basic concepts. I will post technical descriptions and my own interpretation.

API

An application programming interface (API) is a way for two or more computer programs to communicate with each other. It is a type of software interface, offering a service to other pieces of software. Źródło Wikipedia.

My interpretation: API is a set of programming interfaces whose feature is that they indicate that something can be done, but do not tell how to do it. For example, JPA "says that" you can persist Java objects, but does not indicate how to persist, a specific implementation of JPA, e.g. Hibernate, contains the code that writes to the database.

JPA

Jakarta Persistence (JPA; formerly Java Persistence API) is a Jakarta EE application programming interface specification that describes the management of relational data in enterprise Java applications. Source Wikipedia.

My interpretation: JPA contains a set of annotations that can be used to create a Java class definition that reflects a table in the database - entity, @Entity. Provides a set of interfaces with methods for persisting data - EntityManager, e.g. the persist() method.

ORM

Object–relational mapping (ORM, O/RM, andO/R mapping tool) in computer science is a programming technique for converting data between a relational database and the heap of an object-oriented programming language. Source Wikipedia.

My interpretation: The mechanism of mapping Java classes to tables in the database - based on the definition - of the entity, @Entity - contained in the Java class.

What is Hibernate?

I'll start with my own interpretation. Hibernate is an ORM that allows you to map Java classes to database tables. Hibernate is one of the JPA implementations. It is worth remembering that Hibernate uses Java JDBC in its implementation. Knowledge of Hibernate does not exempt you from knowledge of JDBC and SQL.

On the official website Hibernate will read: “Hibernate ORM. Domain model persistence for relational databases.” About Hibernate ORM we will read: "Object/Relational Mapping. Hibernate ORM enables developers to more easily write applications whose data outlives the application process. As an Object/Relational Mapping (ORM) framework, Hibernate is concerned with data persistence as it applies to relational databases (via JDBC).”

Why ORM?

Why do we map Java classes to database tables? The first thing that comes to mind is saving the state of Java objects to the database. How is it possible that Java classes can be "databased"?

The first issue is ORM - you can read more about it at "Hibernate ORM – What is Object/Relational Mapping?” The second issue is , that Java classes are very similar to database tables. Like, then? Java classes are like tables? Explanation below.

Java is an object-oriented language, there are dependencies, associations and connections between classes. However, the databases used by Hibernate ORM are relational databases, there are relationships between tables using a primary key and a foreign key.


public class Address {
    private Long id;
    private String street;
}
CREATE TABLE ADDRESSES (
    ID BIGINT PRIMARY KEY,
    STREET VARCHAR(255)
)

The above Java class Address and the database table ADDRESSES look similar if they use "simple types", things are different when the class has dependencies and the table shows relationships.


public class Client {
    private Long id;
    private String name;
    private Address address;
}
CREATE TABLE CLIENTS (
    ID BIGINT PRIMARY KEY,
    NAME VARCHAR(255),
    ADDRESS_ID BIGINT,
    CONSTRAINT FK_CLIENTS_ADDRESSES 
        FOREIGN KEY (ADDRESS_ID) 
        REFERENCES ADDRESSES(ID)
)

The above Java class Client has a dependency on another class Address. However, in the database table CLIENTS there is a foreign key - FOREIGN KEY - to the primary key - PRIMARY KEY - in the table ADDRESSES. The foreign key is named FK_CLIENTS_ADDRESSES and the primary key is for the ID column.


How to use Hibernate

Całość kodu można znaleźć na moim koncie GitHub

https://github.com/juniorjavadeveloper-pl/hibernate-examples

Oficjalna dokumentacja – Getting Started with Hibernate

https://docs.jboss.org/hibernate/orm/6.4/quickstart/html_single/

Java to TABLE mapping – @Entity

Configuring Hibernate and how to use it is one thing, but the end result is that we are writing something to a relational database. We need to map the Java class to database tables, for this we use entities, classes marked with the annotation @Entity. Such classes constitute a bridge, a template , a definition for Hibernate that will do all the work for us that until now we had to do ourselves using pure JDBC.

@Entity
@Table(name = "ZOO")
public class Animal {
    @Id
    @GeneratedValue
    private Long id;

    @Column(name = "BIRTH_DATE", nullable = false)
    private Date date;
    private String name;

    public Animal() {
    }
}

Which was the first way to use Hibernate? ChatGPT response

Which came first, the egg or the chicken? A similar question can be asked about Hibernate.

Which was the first way to use Hibernate?

Hibernate was the first solution as a standalone ORM framework with a native API (2001). The JPA specification was introduced (2006) later to standardize ORMs in Java, and Hibernate eventually integrated JPA support (2010). The Spring Data project then introduced Spring Data JPA (2011) to simplify data access in Spring applications using JPA.
  • 2001 r. – Native Hibernate API.
  • 2006 r. – JPA specification.
  • 2010 r. – JPA-standard API w Hibernate.
  • 2011 r. – Spring Data JPA.

Native Hibernate API – SessionFactory

One of the available options is the Native Hibernate API. In short, to use the Native Hibernate API we need a SessionFactory – Hibernate Native Bootstrapping.

SessionFactory

Javadoc: The main contract here is the creation of Session instances. Usually an application has a single SessionFactory instance and threads servicing client requests obtain Session instances from this factory.

The internal state of a SessionFactory is immutable. Once it is created this internal state is set. This internal state includes all of the metadata about Object/Relational Mapping.

Implementors must be threadsafe.
public class HibernateNativeBasicConfigurationTest {
    private SessionFactory sessionFactory;

    @BeforeEach
    void setUp() {
        StandardServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder()
                .configure()
                .build();

        try {
            sessionFactory = new MetadataSources(serviceRegistry)
                    .buildMetadata()
                    .buildSessionFactory();
        } catch (Exception e) {
            e.printStackTrace();
            StandardServiceRegistryBuilder.destroy(serviceRegistry);
        }
    }

    @AfterEach
    void tearDown() {
        if (sessionFactory != null) {
            sessionFactory.close();
        }
    }
}

Code on GitHub -> HibernateNativeBasicConfigurationTest

In addition to SessionFactory, we also need Hibernate configuration, e.g. in the form of an XML file - hibernate.cfg.xml. Below is a fragment of the configuration with a link to GitHub with the full content of the configuration file.

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <!-- https://github.com/juniorjavadeveloper-pl/hibernate-examples/blob/master/src/test/resources/hibernate.cfg.xml -->
    </session-factory>
</hibernate-configuration>

JPA-standard API – EntityManager

Another option is the JPA-standard API, which is the implementation of the Java/Jakarta Persistence API specification in Hibernate. In short, to use the JPA-standard API we need EntityManager – Hibernate Jakarta Persistence Bootstrapping.

EntityManager

Javadoc: Interface used to interact with the persistence context.

An EntityManager instance is associated with a persistence context. A persistence context is a set of entity instances in which for any persistent entity identity there is a unique entity instance. Within the persistence context, the entity instances and their lifecycle are managed. The EntityManager API is used to create and remove persistent entity instances, to find entities by their primary key, and to query over entities.

The set of entities that can be managed by a given EntityManager instance is defined by a persistence unit. A persistence unit defines the set of all classes that are related or grouped by the application, and which must be colocated in their mapping to a single database.
public class HibernateJpaBasicConfigurationTest {
    private EntityManagerFactory entityManagerFactory;

    @BeforeEach
    void setUp() {
        entityManagerFactory = Persistence.createEntityManagerFactory(
                "pl.juniorjavadeveloper.examples.hibernate.basic.configuration.pu");
    }

    @AfterEach
    void tearDown() {
        if (entityManagerFactory != null) {
            entityManagerFactory.close();
        }
    }
}

Code on GitHub -> HibernateJpaBasicConfigurationTest

Poza EntityManager potrzebujemy jeszcze konfiguracje Java/Jakarta Persistence np.: w postaci pliku XML – persistence.xml. Below is a fragment of the configuration with a link to GitHub with the full content of the configuration file.

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
             version="2.0">
    <persistence-unit name="pl.juniorjavadeveloper.examples.hibernate.basic.configuration.pu">
        <!-- https://github.com/juniorjavadeveloper-pl/hibernate-examples/blob/master/src/test/resources/META-INF/persistence.xml -->
    </persistence-unit>
</persistence>

Spring Data JPA – JpaRepository / CrudRepository

Using the Spring Framework, we can add the Spring Data JPA module to simplify data access in Spring applications using JPA.

Repository

Javadoc: Central repository marker interface. Captures the domain type to manage as well as the domain type’s id type. General purpose is to hold type information as well as being able to discover interfaces that extend this one during classpath scanning for easy Spring bean creation.

Domain repositories extending this interface can selectively expose CRUD methods by simply declaring methods of the same signature as those declared in CrudRepository.
@Repository
public interface AnimalRepository extends JpaRepository<Animal, Long> {
}

Oficjalny tutorial -> Accessing Data with JPA

Spring Boot provides us with default configuration in the form of a properties file – application.properties. Below is an example of a configuration.

spring.datasource.url=jdbc:h2:~/hibernate-examples
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=update

Where to use Hibernate

Below I will briefly describe where, from my experience, different ways of using Hibernate can be used.

Native – stand-alone applications

If we want a "light" application and don't need all the "baggage" related to Java EE and Spring Framework, Hibernate Native API is completely enough. I used this option to create desktop applications, e.g. in JavaFX or for separate, independent application modules.

JPA-standard – aplikacje Java EE

When creating Java EE applications, we can use JPA, but then we need to add a JPA implementation (JAR file), maybe Hibernate. We place applications for Java EE on application servers that may have a JPA implementation available, then we simply replace it with Hibernate.

Spring Data JPA – applications for Spring Framework

The answer to this question is obvious – applications for Spring Framework.

Does anyone use Hibernate at all?

Watch out, there's a grenade at the end! In my commercial experience, Hibernate is not used in production systems. How is this possible?! Unfortunately, Hibernate is slow and inefficient, with too much overhead of generic concepts and solutions. It is a combine harvester for everything and for every database. Hibernate is very good for rapid prototyping and creating proof-of-concept applications. And of course, to create CRUD applications for our portfolio for recruitment purposes 😉 If not Hibernate, then what? Of course, good old JDBC and/or Spring JdbcTemplate.

Lesson: JDBC Basics (The Java™ Tutorials > JDBC Database Access)

In this lesson you will learn the basics of the JDBC API. Źródło: https://docs.oracle.com/javase/tutorial/jdbc/basics/index.html

Accessing Relational Data using JDBC with Spring

You will build an application that uses Spring’s JdbcTemplate to access data stored in a relational database. Źródło: https://spring.io/guides/gs/relational-data-access/

Summary

I believe that the grenade thrown at the end will not discourage you from using Hibernate. Once we understand the capabilities of Hibernate, it will be easier to adapt a specific way of using Hibernate to our needs. I hope that people learning Hibernate will no longer drown in the sea of possibilities offered by Hibernate when looking for solutions on the Internet. Using Hibernate, you can create proof-of-concept applications easily and quickly.