Learnitweb

Mapping LOBs in Hibernate

1. Introduction

In this tutorial, we’ll discuss mapping LOBs or database Large Objects. We’ll discuss following LOBs:

  • Blob – Binary Large Object. It is used to store large binary data like image, audio, video etc.
  • Clob – Character Large Object. It is used to store large character data like large text file.

We’ll not discuss the NCLOB in this tutorial. NCLOB stores character data encoded in the national character set.

Mapping LOBs (database Large Objects) can be done in 2 ways:

  • Using the JDBC locator types. A LOB locator is only valid during the duration of the transaction in which it was obtained. JDBC LOB locators allow you to efficiently access the LOB data. JDBC LOB locators allow the JDBC driver to stream parts of the LOB data as needed resulting in efficient memory usage.
  • Materializing the LOB data and map it to byte[] or String. There is a performance tradeoff in this case as this option deals with the entire LOB contents in memory.

2. JDBC LOB locator types

The JDBC LOB locator types include:

  • java.sql.Blob – This represents a mapping in Java for an SQL BLOB value. An SQL BLOB is a built-in type that stores a Binary Large Object as a column value in row of database column. By default drivers implement this interface using an SQL locator (BLOB). An SQL locator is a logical pointer to the SQL BLOB data rather than the data itself.
  • java.sql.Clob – This represents the mapping in Java for an SQL CLOB type. An SQL CLOB is a built-in type that stores a Character Large Object as a column value in a row of a database table. By default drivers implement this interface using an SQL locator(CLOB). An SQL locator is a logical pointer to the SQL CLOB data rather than the data itself.
  • java.sql.NClob – This represents the mapping in the Java for the SQL NCLOB type. An SQL NCLOB is a built-in type that stores a Character Large Object using the National Character Set as a column value in a row of a database table.

JDBC handling with LOB data varies from driver to driver. Hibernate takes care of this for you.

3. Example

In this example, we’ll create an entity Article and will show how to save the CLOB and BLOB data.

For this example, we have used the following dependencies in pom.xml:

<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
	<version>8.0.27</version>
</dependency>

<dependency>
	<groupId>org.hibernate</groupId>
	<artifactId>hibernate-core</artifactId>
	<version>5.6.3.Final</version>
</dependency>

The hibernate.cfg.xml looks like the following:

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>

    <session-factory>

        <!-- Database connection settings -->
        <property name="connection.driver_class">com.mysql.cj.jdbc.Driver</property>
        <property name="connection.url">jdbc:mysql://localhost:3306/mydb</property>
        <property name="connection.username">root</property>
        <property name="connection.password">admin</property>

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

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

        <!-- Drop and re-create the database schema on startup -->
        <property name="hbm2ddl.auto">create</property>
		
		<mapping class="com.learnitweb.entity.Article"/>
    </session-factory>

</hibernate-configuration>

Following is the Article entity for our example:

package com.learnitweb.entity;

import java.sql.Clob;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Lob;

@Entity(name = "Article")
public class Article {
	@Id
	private Integer id;

	private String title;

	@Lob
	private Clob articleText;
	
	// getter and setter removed for brevity

}

Notice the use of @Lob annotation and Clob type for articleText field in the Article entity.

Its time to test our code now.

package com.learnitweb.client;

import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.engine.jdbc.ClobProxy;

import com.learnitweb.entity.Article;
import com.learnitweb.util.HibernateUtil;

public class Client {

	public static void main(String[] args) {
		Transaction transaction = null;
		try (Session session = HibernateUtil.getSessionFactory().openSession()) {
			transaction = session.beginTransaction();

			String text = "This is article text.";

			final Article article = new Article();
			article.setId(1);
			article.setTitle("My first article");
			article.setArticleText(ClobProxy.generateProxy(text));

			session.save(article);
			transaction.commit();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

Hibernate will generate the following SQL statements:

create table Article (id integer not null, articleText longtext, title varchar(255), primary key (id))

insert into Article (articleText, title, id) values (?, ?, ?)

binding parameter [1] as [CLOB] - [{clob}]
binding parameter [2] as [VARCHAR] - [My first article]
binding parameter [3] as [INTEGER] - [1]

To retrieve the value, you can use java.io.Reader.

// get Article from DB
Article article = session.get(Article.class, 1);
			
// read the CLOB value from Article 
try (Reader reader = article.getArticleText().getCharacterStream()) {
	String articleText = getDataFromReader(reader);
	System.out.println(articleText);
} catch (Exception e) {
	e.printStackTrace();
}
static String getDataFromReader(Reader reader) throws IOException {
	String targetString = "";
	int intValueOfChar;
	while ((intValueOfChar = reader.read()) != -1) {
		targetString += (char) intValueOfChar;
	}
	return targetString;
}

4. Map CLOB in materialized form

You could also map the CLOB in a materialized form. You can either use a String or a char[].

4.1 CLOB – materialized char[] mapping

@Entity(name = "Article")
public class Article {
	@Id
	private Integer id;

	private String title;

	@Lob
	private char[] articleText;
	
	// getter and setter removed for brevity
	
}

4.2 CLOB – materialized String mapping

@Entity(name = "Article")
public class Article {
	@Id
	private Integer id;

	private String title;

	@Lob
	private String articleText;
	
	// getter and setter removed for brevity
	
}

5. Mapping BLOB

In our example, we’ll save an image in database. Add cover field in the entity Article:

@Entity(name = "Article")
public class Article {
	@Id
	private Integer id;

	private String title;

	@Lob
	private Blob cover;
}

Let us now run the code.

import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.nio.file.Files;

//other imports removed for brevity

public class Client {

	public static void main(String[] args) {
		Transaction transaction = null;
		try (Session session = HibernateUtil.getSessionFactory().openSession()) {
			transaction = session.beginTransaction();

			File fi = new File("D:\\myImage.jpg");
			byte[] fileContent = Files.readAllBytes(fi.toPath());

			final Article article = new Article();
			article.setId(1);
			article.setTitle("My first article");
			article.setCover(BlobProxy.generateProxy(fileContent));

			session.save(article);
			transaction.commit();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

Hibernate will generate the following SQL statements:

create table Article (id integer not null, cover longblob, title varchar(255), primary key (id))

insert into Article (cover, title, id) values (?, ?, ?)

binding parameter [1] as [BLOB] - [{blob}]
binding parameter [2] as [VARCHAR] - [My first article]
binding parameter [3] as [INTEGER] - [1]	

You can do the following while reading the data from database:

Article article = session.get(Article.class, 1);

try (InputStream inputStream = product.getImage().getBinaryStream()) {
    // write your logic here to work with inputStream
}

5.1 BLOB – materialized byte[] mapping

You could also map the BLOB in a materialized form byte[].

@Entity(name = "Article")
public class Article {
	@Id
	private Integer id;

	private String title;

	@Lob
	private byte[] cover;
}