MOBI BOOT CAMP CORP. logoLearning Buddy
  • SIGN IN
  • Introduction
  • 1. Build Tools & Project Structure
  • 2. The Web Layer (Servlets & JSP)
  • 3. Design Patterns & Architecture
  • 4. Persistence Foundations (SQL & JDBC)
  • 5. Object-Relational Mapping (ORM)
    • JPA Specification
    • Hibernate ORM
    • Spring Data JPA
    • Cloud SQL Demo
    • Datastore Relations
    • Spring Data Datastore
  • 6. Modern Web Services & Microservices
  • 7. Hands-on Project

Hibernate

Hibernate is one of the most popular ORM libraries that provides a JPA implementation. This is an open-source library that comes with many other dependent libraries.

The main building blocks of this implementation are summarized below:

  • hibernate.cfg.xml - the XML configuration file that contains the database connection values, among other details.
  • JavaBeanClass.hbm.xml - one configuration file for each of the Java classes that are mapped to the database table(s).
  • HibernateUtils - A utility class that creates and provides a Hibernate Session.
  • Instead of using an *.hbm.xml file, you could also use annotations for identifying the Java members for building ORM relationships.

Here is the same email save/retrieve functionality using Hibernate.

Step 1

To begin with, create a resources folder under main and then create a file called hibernate.cfg.xml in this resources folder. This file contains the database connection details along with entity mapping details:

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

<hibernate-configuration>
    <session-factory>
        <!-- <property name="hibernate.connection.username">username</property>
        <property name="hibernate.connection.password">password</property> -->
        <property name="hibernate.connection.url">jdbc:derby:test;create=true</property>

        <!-- Enable Hibernate's automatic session context management -->
        <property name="current_session_context_class">thread</property>

        <property name="hibernate.dialect">org.hibernate.dialect.DerbyTenSevenDialect</property>
        <property name="show_sql">true</property>
        <mapping resource="com/mbcc/tutorial/hibernate/Emails.hbm.xml"/>
    </session-factory>
</hibernate-configuration>

The username and password configuration values are commented out as we are using an embedded Derby server without using authentication. Otherwise, you have to set these two variables to help establish a connection to your specific database.

hibernate.connection.url should contain the database URL string.

Step 2

Create the Email class as given below:

package com.mbcc.tutorial.hibernate;

import java.util.Date;
import java.util.Objects;

public class Email {

    private Long Id;
    private String emailText;
    private Date sentDate;

    
    public Long getId() {
        return Id;
    }

    public void setId(Long Id) {
        this.Id = Id;
    }

    public String getEmailText() {
        return emailText;
    }

    public void setEmailText(String emailText) {
        this.emailText = emailText;
    }

    public Date getDate() {
        return sentDate;
    }

    public void setDate(Date date) {
        this.sentDate = date;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Email email = (Email) o;
        return Objects.equals(Id, email.Id) &&
                Objects.equals(emailText, email.emailText) &&
                Objects.equals(sentDate, email.sentDate);
    }

    @Override
    public int hashCode() {
        return Objects.hash(Id, emailText, sentDate);
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("Email{");
        sb.append("Id=").append(Id);
        sb.append(", EmailText='").append(emailText).append('\'');
        sb.append(", Date=").append(sentDate);
        sb.append('}');
        return sb.toString();
    }
}

Step 3

Mapping File For Email Entity

The next important file that was required in the earlier versions of Hibernate is the mapping file. You need one mapping file for each entity. This file describes the column names that are mapped to the Java bean's field names. Here it is:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
    <class name="com.mbcc.tutorial.hibernate.Email" table="email">
        <id name="Id" type="java.lang.Long">
            <column name="Id" />
            <generator class="identity" />
        </id>
        <property name="emailText" type="string">
            <column name="email_text" length="30"/>
        </property>
        <property name="date" type="date">
            <column name="email_date" />
        </property>
    </class>
</hibernate-mapping>

Although it is important to note that this file is not required with an annotation-driven implementation. Instead of creating this file, you could add annotations to accomplish this mapping in the Java class itself. The annotation example is shown later.

Step 4

The HibernateUtils class that creates the SessionFactory is shown below. You need a Hibernate Session to start your data persistence operations, and the SessionFactory provides you with a Session:

package com.mbcc.tutorial.hibernate;

import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HibernateUtils {

    private HibernateUtils() {}

    private static final SessionFactory sessionFactory;

    static {
        try {
            sessionFactory = new Configuration().configure().buildSessionFactory();
        } catch (Throwable ex) {
            System.err.println("Initial SessionFactory creation failed." + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }

    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }

   public static void shutdown() {

        getSessionFactory().close();
    }
}

Step 5

Finally, the EmailService class puts all of these together:

package com.mbcc.tutorial.hibernate;

import java.util.List;

import org.hibernate.Session;

public class EmailService {

    private EmailService() {};

    public static Email getEmailById(Long id) {

    	Email email;
        try (Session session = HibernateUtils.getSessionFactory().openSession()) {
            email = session.get(Email.class, id);
        }

        return email;
    }

    @SuppressWarnings("unchecked")
    public static List<Email> getEmails() {

        List<Email> emails;
        try (Session session = HibernateUtils.getSessionFactory().openSession()) {
            emails = session.createQuery("from Email").list();
        }
        return emails;
    }

    public static void save(Email email) {

        try (Session session = HibernateUtils.getSessionFactory().openSession()) {
            session.beginTransaction();

            session.save(email);

            session.getTransaction().commit();
        }
    }
}

You can test the Hibernate implementation using the Java program with the main method as given below:

package com.mbcc.tutorial.hibernate;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Date;
import java.util.List;

public class HibernateExample {

	private static final String DERBY_URL = "jdbc:derby:test;create=true";

    public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException {
    	
    	
    	Class.forName("org.apache.derby.jdbc.EmbeddedDriver").newInstance();
    	dropTable();
    	createTable();
    	
    	

        Email newEmail = new Email();

        newEmail.setEmailText("body of the email- second example");
        newEmail.setDate(new Date());

        EmailService.save(newEmail);

        List<Email> emails = EmailService.getEmails();

        for (Email myemail : emails) {

            System.out.println(myemail);
        }
        
        Long id = 1L;

        Email email = EmailService.getEmailById(id);

        System.out.println(email);

        HibernateUtils.shutdown();
    }
    
    public static void createTable() throws SQLException {

		String emailTableSql = "CREATE TABLE email (" + "  id int NOT NULL GENERATED ALWAYS AS IDENTITY,"
				+ "  email_text varchar(255) NOT NULL," 
				+ "  email_date timestamp DEFAULT NULL," + "  PRIMARY KEY (id)" + ")";

		Connection conn = DriverManager.getConnection(DERBY_URL);
		Statement stmt = conn.createStatement();
		stmt.execute(emailTableSql);

	}
    
    
    public static void dropTable() throws SQLException {
    	Connection conn = DriverManager.getConnection(DERBY_URL);
		Statement stmt = conn.createStatement();
		stmt.execute("drop table if exists email");
    }
	
}

Annotation-Based Implementation

In this implementation, you do not need the .hbm mapping file for each Entity. Instead, you add appropriate annotations to your class. Here is the same class with annotations:

package com.mbcc.tutorial.hibernate;

import java.util.Date;
import java.util.Objects;

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

@Entity
@Table(name = "Email")
public class Email {

	@javax.persistence.Id
	@GeneratedValue(strategy = GenerationType.SEQUENCE)
	private Long Id;

	@Column(name = "email_text",  nullable = false)
	private String emailText;

	@Column(name = "email_date",  nullable = false)
	private Date sentDate;

	public Long getId() {
		return Id;
	}

	public void setId(Long Id) {
		this.Id = Id;
	}

	public String getEmailText() {
		return emailText;
	}

	public void setEmailText(String emailText) {
		this.emailText = emailText;
	}

	public Date getDate() {
		return sentDate;
	}

	public void setDate(Date date) {
		this.sentDate = date;
	}

	@Override
	public boolean equals(Object o) {
		if (this == o)
			return true;
		if (o == null || getClass() != o.getClass())
			return false;
		Email email = (Email) o;
		return Objects.equals(Id, email.Id) && Objects.equals(emailText, email.emailText)
				&& Objects.equals(sentDate, email.sentDate);
	}

	@Override
	public int hashCode() {
		return Objects.hash(Id, emailText, sentDate);
	}

	@Override
	public String toString() {
		final StringBuilder sb = new StringBuilder("Email{");
		sb.append("Id=").append(Id);
		sb.append(", EmailText='").append(emailText).append('\'');
		sb.append(", Date=").append(sentDate);
		sb.append('}');
		return sb.toString();
	}
}

Reference on JAR files to import to enable annotations: https://docs.jboss.org/hibernate/annotations/3.5/reference/en/html/ch01.html#setup-configuration

A Note on Annotations

@Entity Annotation

The @Entity annotation, added to the Email class at the class level, marks this class as an Entity Bean. An entity bean is a POJO (Plain Old Java Object) that has a one-to-one mapping with an RDBMS table and can be uniquely identified with a primary key. The Java class must define a visible no-argument constructor and setters and getters for all the fields.

@Table Annotation

@Table, also set at the class level, allows you to define the RDBMS table name for your entity bean mapping. If no @Table is defined, the default value (i.e., the class name of the entity) is used for the table.

@javax.persistence.Id

The mapped column for the primary key of the entity is assumed to be the primary key of the primary table.

More on annotations: https://docs.jboss.org/hibernate/annotations/3.4/reference/en/html_single/

Finally, the 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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>hibernate-example</groupId>
	<artifactId>hibernate-example</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<dependencies>

		<dependency>
			<groupId>org.apache.derby</groupId>
			<artifactId>derby</artifactId>
			<version>10.1.1.0</version>
		</dependency>


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

	</dependencies>

	<build>
		<resources>
			<resource>
				<directory>src/main/java</directory>
				<includes>
					<include>**/*.xml</include>
				</includes>
			</resource>
			<resource>
				<directory>src/main/resources</directory>
			</resource>
		</resources>

	</build>
	<properties>
		<maven.compiler.source>1.8</maven.compiler.source>
		<maven.compiler.target>1.8</maven.compiler.target>
	</properties>
</project>

Why are the hashCode() and equals() Methods Overridden?

All objects use the inherited equals() method from the top-level Object class when comparing two objects. Even if two instantiated objects are set with the same values for all their fields, the Object class's equals() method will not consider them equal as they are two separate objects. Although that logic is good in the JVM, in the database, however, if you retrieve two rows that have the same primary key, then they are the same row. Since Entity Java beans represent a single row in the database, you override the equals() method so that when two entities have exactly the same set of field values, they are indeed the same. To maintain the general contract for the hashCode method, which states that equal objects must have equal hash codes, you should also override the hashCode() method. Using an IDE like Eclipse will make overriding these methods a breeze. Select the 'Source --> Generate hashCode() and equals()...' menu item to get these two methods overridden easily.

References:

  • https://docs.jboss.org/hibernate/orm/5.4/quickstart/html_single/#hibernate-gsg-tutorial-basic-test https://docs.jboss.org/hibernate/annotations/3.4/reference/en/html_single/
  • https://zetcode.com/hibernate/derby/
Privacy Policy | Terms & Conditions