Hibernate Batch Processing Tutorial

This tutorial will guide you through setting up and demonstrating batch processing in Hibernate 6 with a MySQL database. We'll create a simple application that performs batch inserts and updates using Hibernate.

Introduction

Batch processing in Hibernate allows you to group multiple operations into a single batch to reduce the number of database round-trips and improve performance. In this tutorial, we will:

  1. Set up a Maven project with Hibernate and MySQL dependencies.
  2. Configure Hibernate.
  3. Create an entity class (Product).
  4. Implement batch processing methods.
  5. Demonstrate batch processing with a sample application.

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 the necessary dependencies for Hibernate and MySQL.

<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-batch-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>

        <!-- MySQL Connector -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.29</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>

1.3 Configure Hibernate

Create a file named hibernate.cfg.xml in the src/main/resources directory to configure Hibernate. This file contains the 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>
        <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
        <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>
        <property name="hibernate.hbm2ddl.auto">update</property>
        <property name="hibernate.show_sql">true</property>

        <!-- Batch processing properties -->
        <property name="hibernate.jdbc.batch_size">20</property>
        <property name="hibernate.order_inserts">true</property>
        <property name="hibernate.order_updates">true</property>
    </session-factory>
</hibernate-configuration>

Replace hibernate_db, root, and password with your MySQL database name and credentials.

Explanation:

  • hibernate.dialect specifies the SQL dialect to be used.
  • hibernate.connection.driver_class specifies the JDBC driver class.
  • hibernate.connection.url specifies the JDBC URL for the database connection.
  • hibernate.connection.username and hibernate.connection.password specify the database credentials.
  • hibernate.hbm2ddl.auto specifies the schema generation strategy.
  • hibernate.show_sql specifies whether to show SQL statements in the logs.
  • hibernate.jdbc.batch_size specifies the batch size for batch processing.
  • hibernate.order_inserts and hibernate.order_updates specify whether to order inserts and updates to improve batching efficiency.

Step 2: Create the Entity Class

Create an entity class Product that will be mapped to a table in the database. 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;

@Entity
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String description;
    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 String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    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 @Id annotation specifies the primary key of the entity.
  • The @GeneratedValue(strategy = GenerationType.IDENTITY) annotation specifies that the primary key is auto-incremented.

Step 3: Create the Hibernate Utility Class

Create a utility class HibernateUtil to manage the Hibernate SessionFactory. This class ensures a single instance of SessionFactory is created and provides a method to close it.

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 {
            // Create the SessionFactory from hibernate.cfg.xml
            return new Configuration().configure().buildSessionFactory();
        } catch (Throwable ex) {
            // Make sure you log the exception, as it might be swallowed
            System.err.println("Initial SessionFactory creation failed." + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }

    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }

    public static void shutdown() {
        // Close caches and connection pools
        getSessionFactory().close();
    }
}

Explanation:

  • The buildSessionFactory method creates the SessionFactory from the hibernate.cfg.xml configuration file.
  • The getSessionFactory method returns the singleton instance of SessionFactory.
  • The shutdown method closes the SessionFactory to release resources.

Step 4: Implement Batch Processing

Create a class ProductService to handle database operations with batch processing. This class includes methods for batch inserting and updating Product entities.

Batch Insert Products

package com.example.service;

import com.example.entity.Product;
import com.example.util.HibernateUtil;
import org.hibernate.Session;
import org.hibernate.Transaction;

public class ProductService {

    public void batchInsertProducts(List<Product> products) {
        Session session = HibernateUtil.getSessionFactory().openSession();
        Transaction transaction = null;

        try {
            transaction = session.beginTransaction();
            for (int i = 0; i < products.size(); i++) {
                session.save(products.get(i));
                if (i % 20 == 0) { // Flush a batch of inserts and release memory.
                    session.flush();
                    session.clear();
                }
            }
            transaction.commit();
        } catch (Exception e) {
            if (transaction != null) {
                transaction.rollback();
            }
            e.printStackTrace();
        } finally {
            session.close();
        }
    }

    // Methods for batch updating products can be added here.
}

Batch Update Products

package com.example.service;

import com.example.entity.Product;
import com.example.util.HibernateUtil;
import org.hibernate.Session;
import org.hibernate.Transaction;

public class ProductService {

    // Batch insert method

    public void batchUpdateProducts(List<Product> products) {
        Session session = HibernateUtil.getSessionFactory().openSession();
        Transaction transaction = null;

        try {
            transaction = session.beginTransaction();
            for (int i = 0; i < products.size(); i++) {
                session.update(products.get(i));
                if (i % 20 == 0) { // Flush a batch of updates and release memory.
                    session.flush();
                    session.clear();
                }
            }
            transaction.commit();
        } catch (Exception e) {
            if (transaction != null) {
                transaction.rollback();
            }
            e.printStackTrace();
        } finally {
            session.close();
        }
    }
}

Explanation:

  • The batchInsertProducts and batchUpdateProducts methods open a Hibernate session and begin a transaction.
  • A loop is used to save or update each product in the list. Every 20 operations (as specified by the batch size), the session is flushed and cleared to ensure the batch is processed and memory is released.
  • The transaction is committed using transaction.commit(), ensuring the changes are saved in the database.

If an exception occurs, the transaction is rolled back using transaction.rollback().

  • The session is closed in the finally block to release resources.

Step 5: Demonstrate Batch Processing

Create a MainApp class to demonstrate the batch processing functionality. This class calls the batch processing methods of ProductService.

package com.example.main;

import com.example.entity.Product;
import com.example.service.ProductService;

import java.util.ArrayList;
import java.util.List;

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

        // Create a list of products to insert
        List<Product> productsToInsert = new ArrayList<>();
        for (int i = 1; i <= 100; i++) {
            Product product = new Product();
            product.setName("Product " + i);
            product.setDescription("Description for product " + i);
            product.setPrice(100.0 + i);
            productsToInsert.add(product);
        }

        // Batch insert products
        System.out.println("Batch inserting products:");
        productService.batchInsertProducts(productsToInsert);

        // Create a list of products to update
        List<Product> productsToUpdate = new ArrayList<>();
        for (int i = 1; i <= 100; i++) {
            Product product = new Product();
            product.setId((long) i);
            product.setName("Updated Product " + i);
            product.setDescription("Updated description for product " + i);
            product.setPrice(200.0 + i);
            productsToUpdate.add(product);
        }

        // Batch update products
        System.out.println("Batch updating products:");
        productService.batchUpdateProducts(productsToUpdate);
    }
}

Explanation of the Code in Step 5

  1. Create a ProductService Instance:

    ProductService productService = new ProductService();
    

    An instance of ProductService is created to call its methods for performing batch processing.

  2. Create a List of Products to Insert:

    List<Product> productsToInsert = new ArrayList<>();
    for (int i = 1; i <= 100; i++) {
        Product product = new Product();
        product.setName("Product " + i);
        product.setDescription("Description for product " + i);
        product.setPrice(100.0 + i);
        productsToInsert.add(product);
    }
    

    A list of 100 products is created for batch insertion.

  3. Batch Insert Products:

    System.out.println("Batch inserting products:");
    productService.batchInsertProducts(productsToInsert);
    

    The batchInsertProducts method is called to insert the products in batches.

  4. Create a List of Products to Update:

    List<Product> productsToUpdate = new ArrayList<>();
    for (int i = 1; i <= 100; i++) {
        Product product = new Product();
        product.setId((long) i);
        product.setName("Updated Product " + i);
        product.setDescription("Updated description for product " + i);
        product.setPrice(200.0 + i);
        productsToUpdate.add(product);
    }
    

    A list of 100 products is created for batch updating.

  5. Batch Update Products:

    System.out.println("Batch updating products:");
    productService.batchUpdateProducts(productsToUpdate);
    

    The batchUpdateProducts method is called to update the products in batches.

Sample Output

When you run the MainApp class, you should see the following output:

Batch inserting products:
Batch updating products:

This output indicates that the batch processing methods were called successfully. You can verify the batch insert and update operations by checking the database.

Conclusion

In this tutorial, we have successfully demonstrated how to implement batch processing in Hibernate using MySQL. We set up a Hibernate project, created an entity class, and implemented batch processing functionality for insert and update operations. This guide provides a solid foundation for optimizing database operations in your Hibernate-based applications.

Comments