0
Sponsored Links


Ad by Google
In our previous post we have seen Hibernate Join Fetching Example and in this post we are going to show you Hibernate Batch Fetching strategy example using annotation.
To improve the performance of queries Hibernate provides the fetching strategies, total four different types of fetching strategies supported by hibernate and those are listed below.

Fetching strategies supported by Hibernate:
  1. Join fetching
  2. Select fetching
  3. Subselect fetching
  4. Batch fetching
Batch fetching: an optimization strategy for select fetching. Hibernate retrieves a batch of entity instances or collections in a single SELECT by specifying a list of primary or foreign keys. To use Batch fetching you have to define batch size using @BatchSize(size=x) batch size allows number of collections to be loaded.

For example, from our one-to-many association example of authors and books we are going to fetch all the authors and associated books. As of now we have only 5 authors in our database and if we are not using batch fetching then hibernate will generate 6(n+1) select queries. 1 query for author table and 5 queries for associated book table. This is the concern why you need batch fetching to reduce the maximum number of database hit. Lets reduce the number of database hit by using batch fetching strategy.

Now if we enable batch fetching something like @BatchSize(size=5), It will only generate two select statement using in query. Please see the output of this project below.

Here is an One-To-Many association mapping of an author and book table ER diagram:

Tools and Technologies we are using here:

  • JDK 7
  • Hibernate 4.3.7
  • MySql 5.1.10
  • Eclipse Juno 4.2
  • Maven 3.2

Main Objects of this project are:
  • pom.xml
  • hibernate.cfg.xml
  • annotated pojo
  • database
Step 1. Create database script.

CREATE DATABASE /*!32312 IF NOT EXISTS*/`hibernate_tutorial`;

USE `hibernate_tutorial`;

/*Table structure for table `author` */

DROP TABLE IF EXISTS `author`;

CREATE TABLE `author` (
  `author_id` bigint(20) NOT NULL AUTO_INCREMENT,
  `first_name` varchar(255) DEFAULT NULL,
  `last_name` varchar(255) DEFAULT NULL,
  `dob` datetime DEFAULT NULL,
  PRIMARY KEY (`author_id`)
) ENGINE=InnoDB AUTO_INCREMENT=101 DEFAULT CHARSET=latin1;

/*Table structure for table `book` */

DROP TABLE IF EXISTS `book`;

CREATE TABLE `book` (
  `book_id` bigint(20) NOT NULL AUTO_INCREMENT,
  `title` varchar(255) DEFAULT NULL,
  `isbn` varchar(255) DEFAULT NULL,
  `author_id` bigint(20) NOT NULL,
  PRIMARY KEY (`book_id`),
  KEY `FK_4sac2ubmnqva85r8bk8fxdvbf` (`author_id`),
  CONSTRAINT `FK_4sac2ubmnqva85r8bk8fxdvbf` FOREIGN KEY (`author_id`) REFERENCES `author` (`author_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1001 DEFAULT CHARSET=latin1;

Step 2. Create a Maven Project:
Step A: Go to File->New->Other..


Step B: Select Maven Project from the select wizard.


Step C: Select project name and location from New Maven Project wizard.


Step D: Configure project, provide GroupId, artifactId etc. See the details from the screenshot. This screen shot is from our old post so here you need to change the artifactId something like Batch-Fetch-Example and also change the group id.


Step E: After completion of all the above steps, now your project will looks like this screenshot.

3. Add project dependencies into pom.xml file:
Double click on your project's pom.xml file it will looks like this with very limited information.
<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.javamakeuse.fetch</groupId>
  <artifactId>Batch-Fetch-Example</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>Batch-Fetch-Example</name>
  
</project>

Now add hibernate and mysql dependencies entry inside pom.xml file. Paste the below code inside the project tag of pom.xml file.
<dependencies>
  <!-- Hibernate Dependency -->
  <dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-core</artifactId>
   <version>4.3.7.Final</version>
  </dependency>
  
  <!-- MySql Connector dependency -->
  <dependency>
   <groupId>mysql</groupId>
   <artifactId>mysql-connector-java</artifactId>
   <version>5.1.10</version>
  </dependency>

 </dependencies>

Here is a complete pom.xml file
<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.javamakeuse.fetch</groupId>
  <artifactId>Batch-Fetch-Example</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>Batch-Fetch-Example</name>
  
  <dependencies>
  <!-- Hibernate Dependency -->
  <dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-core</artifactId>
   <version>4.3.7.Final</version>
  </dependency>
  
  <!-- MySql Connector dependency -->
  <dependency>
   <groupId>mysql</groupId>
   <artifactId>mysql-connector-java</artifactId>
   <version>5.1.10</version>
  </dependency>

 </dependencies>

 <build>
  <plugins>
   <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
     <source>1.7</source>
     <target>1.7</target>
    </configuration>
   </plugin>
  </plugins>
 </build>
</project>
4. Create a hibernate.cfg.xml file:
Create hibernate.cfg.xml file inside src/main/resources folder.

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>
        <!-- Echo all executed SQL to stdout -->
        <property name="show_sql">true</property>
        <property name="format_sql">true</property> 
        <property name="hbm2ddl.auto">update</property>
        <mapping class="com.javamakeuse.fetch.pojo.Author"/>
        <mapping class="com.javamakeuse.fetch.pojo.Book"/>
          
    </session-factory>
</hibernate-configuration>
Step 5. Create an Author annotated class:
package com.javamakeuse.fetch.pojo;

import java.util.Date;
import java.util.HashSet;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

import org.hibernate.annotations.BatchSize;

@Entity
@Table(name="author")
public class Author {

	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	@Column(name="author_id", unique=true, nullable=false)
	private long authorId;
	
	@Column(name="first_name")
	private String firstName;
	
	@Column(name="last_name")
	private String lastName;
	
	@Temporal(TemporalType.DATE)
	@Column(name="dob")
	private Date dateOfBirth;
	
	@OneToMany(mappedBy="author",cascade=CascadeType.ALL,fetch=FetchType.LAZY)
	@BatchSize(size=5)
	private Set<Book> books = new HashSet<>();
	
	public long getAuthorId() {
		return authorId;
	}
	public void setAuthorId(long authorId) {
		this.authorId = authorId;
	}
	public String getFirstName() {
		return firstName;
	}
	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}
	public String getLastName() {
		return lastName;
	}
	public void setLastName(String lastName) {
		this.lastName = lastName;
	}
	public Date getDateOfBirth() {
		return dateOfBirth;
	}
	public void setDateOfBirth(Date dateOfBirth) {
		this.dateOfBirth = dateOfBirth;
	}
	public Set<Book> getBooks() {
		return books;
	}
	public void setBooks(Set<Book> books) {
		this.books = books;
	}
}
Note: In Author class we are using @BatchSize(size=5)

Step 6. Create a Book annotated class

package com.javamakeuse.fetch.pojo;

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

@Entity
@Table(name="book")
public class Book {

 @Id
 @GeneratedValue(strategy=GenerationType.IDENTITY)
 @Column(name="book_id", unique=true, nullable=false)
 private long bookId;
 
 @Column(name="title")
 private String title;
 
 @Column(name = "isbn")
 private String isbn;
 
 @ManyToOne
 @JoinColumn(name = "author_id", nullable=false)
 private Author author;
 
 public long getBookId() {
  return bookId;
 }
 public void setBookId(long bookId) {
  this.bookId = bookId;
 }
 public String getTitle() {
  return title;
 }
 public void setTitle(String title) {
  this.title = title;
 }
 public String getIsbn() {
  return isbn;
 }
 public void setIsbn(String isbn) {
  this.isbn = isbn;
 }
 public Author getAuthor() {
  return author;
 }
 public void setAuthor(Author author) {
  this.author = author;
 }

}
Step 7. Create a HibernateUtility class: HibernateUtility class to build SessionFactory via loading Configuration file.

HibernateUtility .java
package com.javamakeuse.fetch.util;

import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;

public class HibernateUtility {
  private static final SessionFactory sessionFactory = buildSessionFactory();
     private static SessionFactory buildSessionFactory() {
     
      Configuration configuration = new Configuration();
      configuration.configure();

      ServiceRegistry serviceRegistry = new ServiceRegistryBuilder().
      applySettings(configuration.getProperties()).buildServiceRegistry();
      SessionFactory sessionFactory = configuration.buildSessionFactory(serviceRegistry);
   return sessionFactory;
     }
   
     public static SessionFactory getSessionFactory() {
         return sessionFactory;
     }
}

Step 8. Create an AuthorDAO class: to perform insert/update/get/delete records into the database.

AuthorDAO.java
package com.javamakeuse.fetch.dao;

import java.util.List;

import org.hibernate.Session;
import org.hibernate.SessionFactory;

import com.javamakeuse.fetch.pojo.Author;
import com.javamakeuse.fetch.util.HibernateUtility;

public class AuthorDAO {
	private static SessionFactory sessionFactory;
	static {
		sessionFactory = HibernateUtility.getSessionFactory();
	}

	public static List<Author> findAll() {
		Session session = sessionFactory.openSession();
		List<Author> authorList = session.createQuery("from Author").list();
		return authorList;
	}

	public static Author save(Author author) {
		Session session = sessionFactory.openSession();

		session.beginTransaction();

		session.save(author);

		session.getTransaction().commit();

		return author;
	}

	public static Author update(Author author) {
		Session session = sessionFactory.openSession();

		session.beginTransaction();

		session.merge(author);

		session.getTransaction().commit();

		return author;

	}

	public static void delete(Author author) {
		Session session = sessionFactory.openSession();

		session.beginTransaction();

		session.delete(author);

		session.getTransaction().commit();

	}
}
Step 9. Create an AuthorService class: to call the methods of AuthorDAO class.

AuthorService.java
package com.javamakeuse.fetch.service;

import java.util.List;

import com.javamakeuse.fetch.dao.AuthorDAO;
import com.javamakeuse.fetch.pojo.Author;

public class AuthorService {

	public static void main(String[] args) {
		List<Author> authorList = AuthorDAO.findAll();
		for (Author author : authorList) {
			author.getBooks().size();
		}

	}
}

Run AuthorService and see the output it will generate exactly two select statement with in query.

OUT PUT
Hibernate: 
    select
        author0_.author_id as author_i1_0_,
        author0_.dob as dob2_0_,
        author0_.first_name as first_na3_0_,
        author0_.last_name as last_nam4_0_ 
    from
        author author0_
Hibernate: 
    select
        books0_.author_id as author_i4_0_1_,
        books0_.book_id as book_id1_1_1_,
        books0_.book_id as book_id1_1_0_,
        books0_.author_id as author_i4_1_0_,
        books0_.isbn as isbn2_1_0_,
        books0_.title as title3_1_0_ 
    from
        book books0_ 
    where
        books0_.author_id in (
            ?, ?, ?, ?, ?
        )

Now run without using batch fetch remove @BatchSize(size=5) from Author.java and see the output it will generate 6 select queries.

Download the complete example from here Source Code


References:
Reference 1
Reference 2

Sponsored Links

0 comments:

Post a Comment