Hibernate Elasticsearch Tutorial

This tutorial will guide you through setting up and using Hibernate with Elasticsearch, a highly scalable open-source search engine. We will demonstrate how to configure Hibernate to interact with Elasticsearch, index data, and perform search operations.

Introduction

Elasticsearch is a distributed, RESTful search and analytics engine capable of solving a growing number of use cases. Integrating Hibernate with Elasticsearch allows you to leverage Hibernate's ORM capabilities with Elasticsearch's powerful search and analytics features.

In this tutorial, we will:

  1. Set up a Maven project with necessary dependencies.
  2. Configure Hibernate to connect to Elasticsearch.
  3. Create an entity class (Product).
  4. Index data and perform search operations using Hibernate Search.

Step 1: Set Up Your Project

1.1 Create a Maven Project

Open your IDE and create a new Maven project.

1.2 Add Dependencies

Update your pom.xml file to include dependencies for Hibernate, Elasticsearch, and Hibernate Search.

<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>hibernate-elasticsearch-example</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <!-- Hibernate ORM -->
        <dependency>
            <groupId>org.hibernate.orm</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>6.4.0.Final</version>
        </dependency>

        <!-- Hibernate Search -->
        <dependency>
            <groupId>org.hibernate.search</groupId>
            <artifactId>hibernate-search-mapper-orm</artifactId>
            <version>6.1.6.Final</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate.search</groupId>
            <artifactId>hibernate-search-backend-elasticsearch</artifactId>
            <version>6.1.6.Final</version>
        </dependency>

        <!-- Elasticsearch Client -->
        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch-rest-high-level-client</artifactId>
            <version>7.13.4</version>
        </dependency>

        <!-- SLF4J for logging -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.32</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.7.32</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.10.1</version>
                <configuration>
                    <source>21</source>
                    <target>21</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Step 2: Configure Hibernate

2.1 Create hibernate.cfg.xml

Create a hibernate.cfg.xml file in the src/main/resources directory to configure database connection settings and Hibernate properties.

<!DOCTYPE hibernate-configuration PUBLIC
    "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
    <session-factory>
        <!-- JDBC Database connection settings -->
        <property name="hibernate.connection.driver_class">com.mysql.cj.jdbc.Driver</property>
        <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hibernate_db</property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.connection.password">password</property>

        <!-- JDBC connection pool settings -->
        <property name="hibernate.c3p0.min_size">5</property>
        <property name="hibernate.c3p0.max_size">20</property>
        <property name="hibernate.c3p0.timeout">300</property>
        <property name="hibernate.c3p0.max_statements">50</property>
        <property name="hibernate.c3p0.idle_test_period">3000</property>

        <!-- SQL dialect -->
        <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>

        <!-- Echo all executed SQL to stdout -->
        <property name="hibernate.show_sql">true</property>
        <property name="hibernate.format_sql">true</property>

        <!-- Drop and re-create the database schema on startup -->
        <property name="hibernate.hbm2ddl.auto">update</property>

        <!-- Hibernate Search properties -->
        <property name="hibernate.search.backend.elasticsearch.hosts">http://localhost:9200</property>
        <property name="hibernate.search.backend.type">elasticsearch</property>
        <property name="hibernate.search.backend.version">7.10</property>

        <!-- Entities -->
        <mapping class="com.example.entity.Product"/>
    </session-factory>
</hibernate-configuration>

Replace hibernate_db, root, and password with your MySQL database name and credentials. Replace http://localhost:9200 with your Elasticsearch server URL.

Explanation:

  • hibernate.connection.driver_class specifies the JDBC driver class for MySQL.
  • hibernate.connection.url specifies the JDBC URL for the database connection.
  • hibernate.connection.username and hibernate.connection.password specify the database credentials.
  • hibernate.c3p0 properties configure the connection pool settings using C3P0.
  • hibernate.dialect specifies the SQL dialect to be used.
  • hibernate.show_sql and hibernate.format_sql properties are used to display and format the generated SQL statements.
  • hibernate.hbm2ddl.auto specifies the schema generation strategy.
  • hibernate.search.backend.elasticsearch.hosts, hibernate.search.backend.type, and hibernate.search.backend.version specify the Elasticsearch backend configuration for Hibernate Search.
  • The <mapping class="com.example.entity.Product"/> line maps the Product entity to the database.

Step 3: Create the Entity Class

Create an entity class Product that will be indexed by Elasticsearch. This class uses annotations to define the entity and its fields.

package com.example.entity;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.FullTextField;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.Indexed;

@Entity
@Indexed
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @FullTextField
    private String name;

    private double price;

    // Getters and setters
    public Long getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }
}

Explanation:

  • The @Entity annotation specifies that the class is an entity and is mapped to a database table.
  • The @Indexed annotation specifies that the entity will be indexed by Elasticsearch.
  • The @FullTextField annotation specifies that the field will be indexed for full-text search.
  • The @Id annotation specifies the primary key of the entity.
  • The @GeneratedValue(strategy = GenerationType.IDENTITY) annotation specifies that the primary key is auto-incremented.

Step 4: Create the DAO Class

Create a DAO class to manage database operations and indexing using Hibernate.

4.1 Create ProductDAO

package com.example.dao;

import com.example.entity.Product;
import com.example.util.HibernateUtil;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.search.mapper.orm.Search;
import org.hibernate.search.mapper.orm.session.SearchSession;

import java.util.List;

public class ProductDAO {

    public void saveProduct(Product product) {
        Transaction transaction = null;
        try (Session session = HibernateUtil.getSessionFactory().openSession()) {
            transaction = session.beginTransaction();
            session.save(product);
            transaction.commit();
        } catch (Exception e) {
            if (transaction != null) {
                transaction.rollback();
            }
            e.printStackTrace();
        }
    }

    public List<Product> searchProductByName(String name) {
        try (Session session = HibernateUtil.getSessionFactory().openSession()) {
            SearchSession searchSession = Search.session(session);
            return searchSession.search(Product.class)
                .where(f -> f.match().field("name").matching(name))
                .fetchAllHits();
        }
    }

    public List<Product> getAllProducts() {
        try (Session session = HibernateUtil.getSessionFactory().openSession()) {
            return session.createQuery("from Product", Product.class).list();
        }
    }

    public void deleteProduct(Long id) {
        Transaction transaction = null;
        try (Session session = HibernateUtil.getSessionFactory().openSession()) {
            transaction = session

.beginTransaction();
            Product product = session.get(Product.class, id);
            if (product != null) {
                session.delete(product);
                System.out.println("Product is deleted");
            }
            transaction.commit();
        } catch (Exception e) {
            if (transaction != null) {
                transaction.rollback();
            }
            e.printStackTrace();
        }
    }
}

Explanation:

  • The ProductDAO class contains methods to interact with the database and Elasticsearch using Hibernate.
  • The saveProduct method saves a new product to the database and indexes it in Elasticsearch.
  • The searchProductByName method searches for products by name using full-text search in Elasticsearch.
  • The getAllProducts method retrieves all products from the database.
  • The deleteProduct method deletes a product by its ID.

Step 5: Demonstrate Indexing and Search Operations

Create a main class to demonstrate indexing and search operations using the Product entity and ProductDAO class.

5.1 Create MainApp

package com.example.main;

import com.example.dao.ProductDAO;
import com.example.entity.Product;
import com.example.util.HibernateUtil;

import java.util.List;

public class MainApp {
    public static void main(String[] args) {
        ProductDAO productDAO = new ProductDAO();

        // Create new products
        Product product1 = new Product();
        product1.setName("Laptop");
        product1.setPrice(1000.00);

        Product product2 = new Product();
        product2.setName("Phone");
        product2.setPrice(500.00);

        // Save products
        productDAO.saveProduct(product1);
        productDAO.saveProduct(product2);

        // Search products by name
        List<Product> searchResults = productDAO.searchProductByName("Laptop");
        System.out.println("Search Results for 'Laptop':");
        searchResults.forEach(product -> System.out.println(product.getName() + " - " + product.getPrice()));

        // Get all products
        List<Product> products = productDAO.getAllProducts();
        System.out.println("All Products:");
        products.forEach(product -> System.out.println(product.getName() + " - " + product.getPrice()));

        // Delete product
        productDAO.deleteProduct(product2.getId());

        // Get all products after deletion
        products = productDAO.getAllProducts();
        System.out.println("All Products after deletion:");
        products.forEach(product -> System.out.println(product.getName() + " - " + product.getPrice()));

        // Shut down the SessionFactory
        HibernateUtil.shutdown();
    }
}

Explanation:

  • The MainApp class demonstrates indexing and search operations using the ProductDAO class.
  • The saveProduct method is called to save new products to the database and index them in Elasticsearch.
  • The searchProductByName method is called to search for products by name using full-text search in Elasticsearch.
  • The getAllProducts method is called to retrieve all products from the database and display them.
  • The deleteProduct method is called to delete a product by its ID.
  • The getAllProducts method is called again to retrieve all products after the deletion.

5.2 Create HibernateUtil Class

Create a utility class HibernateUtil to manage the Hibernate SessionFactory.

package com.example.util;

import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HibernateUtil {
    private static final SessionFactory sessionFactory = buildSessionFactory();

    private static SessionFactory buildSessionFactory() {
        try {
            // Load the configuration and build the SessionFactory
            return new Configuration().configure().buildSessionFactory();
        } catch (Throwable ex) {
            System.err.println("Initial SessionFactory creation failed." + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }

    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }

    public static void shutdown() {
        getSessionFactory().close();
    }
}

Explanation:

  • The HibernateUtil class provides a singleton SessionFactory and a method to shut it down.
  • The buildSessionFactory method loads the Hibernate configuration from hibernate.cfg.xml and builds the SessionFactory.

Step 6: Run the Application

  1. Ensure your MySQL database and Elasticsearch server are running and the connection details in hibernate.cfg.xml are correct.
  2. Run the MainApp class to load the Hibernate configuration, build the SessionFactory, save products, perform search operations, and print the results.

Sample Output

If everything is set up correctly, running the MainApp class should produce output similar to the following:

Search Results for 'Laptop':
Laptop - 1000.0
All Products:
Laptop - 1000.0
Phone - 500.0
Product is deleted
All Products after deletion:
Laptop - 1000.0

Conclusion

In this tutorial, we have successfully demonstrated how to integrate Hibernate with Elasticsearch, a highly scalable search and analytics engine. We configured the project dependencies, created an entity class, set up the Hibernate configuration file, and demonstrated indexing and search operations using Hibernate Search and Elasticsearch. This guide provides a solid foundation for using Hibernate with Elasticsearch in your applications.


Comments