Hibernate/JPA Batch Insert and Batch Update Example

This tutorial shows how to create batch insert and batch update statements using JPA and Hibernate. JDBC offers support for batching together SQL statements that can be represented as a single PreparedStatement. The JDBC driver will send the batched operation to the server in one call. This can potentially lead in a reduction to network bandwidth usage.

Maven Dependencies

We use Apache Maven to manage the projects dependencies.

<?xml version="1.0" encoding="UTF-8"?>
<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.memorynotfound.db.hibernate</groupId>
    <artifactId>jpa-named-stored-procedure</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>war</packaging>
    <name>HIBERNATE - ${project.artifactId}</name>
    <url>http://memorynotfound.com</url>

    <properties>
        <mysql.driver.version>6.0.4</mysql.driver.version>
        <hibernate.version>5.2.3.Final</hibernate.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.driver.version}</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>${hibernate.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.5.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

Create Model + Mappings

package com.memorynotfound.hibernate;

import javax.persistence.*;

@Entity
public class Book {

    @Id
    private Integer id;
    private String title;

    public Book() {
    }

    public Book(Integer id, String title) {
        this.id = id;
        this.title = title;
    }

    public Integer getId() {
        return id;
    }

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

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    @Override
    public String toString() {
        return "Book{" +
                "id=" + id +
                ", title='" + title + '\'' +
                '}';
    }
}

If you prefer XML over Annotations, you can use the equivalent JPA XML mapping. This file is located in the src/main/resources/META-INF folder and is named orm.xml.

<?xml version="1.0" encoding="UTF-8" ?>
<entity-mappings xmlns="http://xmlns.jcp.org/xml/ns/persistence/orm"
                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                 xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence/orm
                                     http://xmlns.jcp.org/xml/ns/persistence/orm_2_0.xsd" version="2.1">
    <!-- entity mapping -->
    <entity class="com.memorynotfound.hibernate.Book">
        <attributes>
            <id name="id"/>
        </attributes>
        <attributes>
            <basic name="title"/>
        </attributes>
    </entity>

</entity-mappings>

Hibernate/JPA Configuration

We configure the JPA Persistence Unit using the persistence.xml file, which is located in the src/main/resources/META-INF directory.

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
                                 http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
    <persistence-unit name="mnf-pu" transaction-type="RESOURCE_LOCAL">
        <!-- enable if you want xml mappings
        <mapping-file>META-INF/orm.xml</mapping-file>
        -->
        <properties>
            <!-- Configuring JDBC properties -->
            <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/memorynotfound?serverTimezone=Europe/Brussels"/>
            <property name="javax.persistence.jdbc.user" value="root"/>
            <property name="javax.persistence.jdbc.password" value=""/>
            <property name="javax.persistence.jdbc.driver" value="com.mysql.cj.jdbc.Driver"/>

            <!-- Hibernate properties -->
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
            <property name="hibernate.hbm2ddl.auto" value="create-drop"/>
            <property name="hibernate.format_sql" value="false"/>
            <property name="hibernate.show_sql" value="true"/>
            
            <!-- batching size -->
            <property name="hibernate.jdbc.batch_size" value="30"/>
            <property name="hibernate.order_inserts" value="true"/>
            <property name="hibernate.order_updates" value="true"/>
            <property name="hibernate.jdbc.batch_versioned_data" value="true"/>

        </properties>
    </persistence-unit>
</persistence>
  • hibernate.jdbc.batch_size Controls the maximum number of statements Hibernate will batch together before asking the driver to execute the batch. Zero or a negative number disables this feature.
  • hibernate.jdbc.batch_versioned_data Some JDBC drivers return incorrect row counts when a batch is executed. If your JDBC driver falls into this category this setting should be set to false. Otherwise, it is safe to enable this which will allow Hibernate to still batch the DML for versioned entities and still use the returned row counts for optimistic lock checks. Since 5.0, it defaults to true. Previously (versions 3.x and 4.x), it used to be false.
  • hibernate.order_updates Forces Hibernate to order SQL updates by the entity type and the primary key value of the items being updated. This allows for more batching to be used. It will also result in fewer transaction deadlocks in highly concurrent systems. Comes with a performance hit, so benchmark before and after to see if this actually helps or hurts your application.
  • hibernate.order_insertsForces Hibernate to order inserts to allow for more batching to be used. Comes with a performance hit, so benchmark before and after to see if this actually helps or hurts your application.

Create, Run and Test

Use the flush() and clear() methods of the EntityManager regularly, to control the size of the first-level cache. Otherwise an OutOfMemoryException can occur when the maximum size is reached. To enable batch processing, it is important that you set the FlushMode to FlushModeType.COMMIT.

package com.memorynotfound.hibernate;

import javax.persistence.*;
import java.util.List;

public class App {

    public static void main (String...args) throws InterruptedException {

        EntityManagerFactory emf = Persistence.createEntityManagerFactory("mnf-pu");
        EntityManager em = emf.createEntityManager();
        em.setFlushMode(FlushModeType.COMMIT);

        int batchSize = 30;


        // - - - - - - - - - - - - - - Hibernate/JPA Batch Insert example - - - - - - - - - - - -
        em.getTransaction().begin();
        for (int i = 0; i < 100; i++){
            Book book = new Book(i, "Hibernate/JPA Batch Insert Example: " + i);
            em.persist(book);

            if (i % batchSize == 0 && i > 0) {
                em.flush();
                em.clear();
            }
        }
        em.getTransaction().commit();



        // - - - - - - - - - - - - - - Hibernate/JPA Batch Update example - - - - - - - - - - - -
        List<Book> books = em.createQuery("From Book", Book.class).getResultList();

        em.getTransaction().begin();
        for (int i = 0; i < books.size(); i++){
            Book book = books.get(i);
            book.setTitle("Hibernate/JPA Batch Update Example: " + i);
            book = em.merge(book);
            em.persist(book);

            if (i % batchSize == 0 && i > 0) {
                em.flush();
                em.clear();
            }
        }
        em.getTransaction().commit();

        emf.close();
    }
}

References

Download

You may also like...