2
Sponsored Links


Ad by Google
In our previous post we have seen Hibernate Interceptor Example and in this post we are going show you the implementation of Hibernate Batch insertion.

Hibernate Batch Processing: Allows to add multiple statement into a batch and execute that batch by making a single round trip to the database. Hibernate batch insert/update will not generate a single a insert/update statement, It'll generate the multiple statement into a single round trip to database.

Batch insert will reduce your performance comparing to native approach. For example suppose we would like to insert 100000000 product into the database, So how we do it in native way.

Native approach:
session.beginTransaction();
  for (int i = 0; i < 1000000000; i++) {
    Product product = new Product("P"+i);
 session.save(product);
    }
session.getTransaction().commit();

The above will may be fall with an OutOfMemoryException on various systems depends on the configuration of the system. If not fall with an exception then it will take too much time to insert the records into the database.

Why fall with an OutOfMemoryException : Because it will insert all the the records at last once iteration of 1000000000 is completed, till the iteration it will cache all the newly inserted records into the session-level cache and session level cache is in-memory somewhere in JVM and it has limitation to keep the records in memory depend on system to system configuration. Once the limitation crossed it will throws OutOfMemoryException.

Solution of above problem is enable batch insertion, with limited batch size by using below statement you can enable batch.

<property name="hibernate.jdbc.batch_size">30 </property>

If you are using second-level cache then make sure that you disabled the second-level cache, you can disable second-level cache by using below statement.

<property name="hibernate.cache.use_second_level_cache">false</property>


Lets see an example,Create product table script.

CREATE DATABASE `hibernate_tutorial`;

USE `hibernate_tutorial`;

/*Table structure for table `product` */

DROP TABLE IF EXISTS `product`;

CREATE TABLE `product` (
  `product_id` bigint(20) NOT NULL AUTO_INCREMENT,
  `product_code` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`product_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1;

Create hibernate.cfg.xml

<?xml version='1.0' encoding='utf-8'?>
<!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="connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="connection.url">jdbc:mysql://localhost:3306/hibernate_tutorial</property>
        <property name="connection.username">root</property>
        <property name="connection.password">root</property>
         
        <property name="dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
   <property name="hibernate.jdbc.batch_size">30 </property> 
        <!-- Echo all executed SQL to stdout -->
        <property name="show_sql">true</property>
        <!-- <property name="format_sql">true</property> -->
        <property name="hbm2ddl.auto">create</property>
        <mapping class="com.javamakeuse.hibernate.batch.pojo.Product"/>
          
    </session-factory>
</hibernate-configuration>

Create Product.java entity class

package com.javamakeuse.hibernate.batch.pojo;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "product")
public class Product {

 @Id
 @GeneratedValue(strategy = GenerationType.IDENTITY)
 @Column(name = "product_id")
 private long productId;

 @Column(name = "product_code")
 private String productCode;

 public Product(String productCode) {
  super();
  this.productCode = productCode;
 }

 public long getProductId() {
  return productId;
 }

 public String getProductCode() {
  return productCode;
 }

}

Create HibernateUtility.java

package com.javamakeuse.hibernate.batch.util;

import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;

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

 private static SessionFactory buildSessionFactory() {
  Configuration configuration = new Configuration();
  configuration.configure();

  ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder()
    .applySettings(configuration.getProperties()).build();
  SessionFactory sessionFactory = configuration
    .buildSessionFactory(serviceRegistry);

  return sessionFactory;
 }

 public static SessionFactory getSessionFactory() {
  return sessionFactory;
 }

}

Create ProductService.java class to insert 1000000000 rows into the database without falling any exception.

package com.javamakeuse.hibernate.batch.service;

import org.hibernate.Session;

import com.javamakeuse.hibernate.batch.pojo.Product;
import com.javamakeuse.hibernate.batch.util.HibernateUtility;

public class ProductService {
 public static void main(String[] args) {
  Session session = HibernateUtility.getSessionFactory().openSession();

  session.beginTransaction();
  for (int i = 0; i < 1000000000; i++) {
   Product product = new Product("P" + i);
   session.save(product);
   if (i % 30 == 0) {
    // flush a batch of inserts and release memory:
    session.flush();
    session.clear();
   }
  }
  session.getTransaction().commit();
 }
}

OUT PUT:
It will insert 1000000000 records into the product table.

done :)


References:
Reference 1
Reference 2
Sponsored Links

2 comments:

  1. Is there an elegant way to access the sessions without having a configuration xml?
    Spring boot tends to load these attributes automatically from application.properties and it doesn't make sense to load them from both.

    ReplyDelete
  2. I think the issue is that hibernate-jpa names things its own way and handles most normal operations its own way.

    By doing what you do in this tutorial you are no longer able to use hibernate's improved naming strategy.

    ReplyDelete