Integrating Hibernate with the Spring Framework

While using the Hibernate framework, you do not write the code to manage the connection or to deal with statements and result sets. Instead, all the details for accessing a particular data source are configured in the XML files and/or in the Java annotations.

While integrating the Hibernate framework with the Spring Framework, the business objects are configured with the help of the IoC container and can be externalized from the application code. Hibernate objects can be used as Spring beans in your application and you can avail all the benefits of the Spring Framework.

In this section, we will set up the Hibernate environment and create a Spring Hibernate project in STS. The simplest way to integrate Hibernate with Spring is to have a bean for SessionFactory and make it a singleton and the DAOs classes just get that bean and inject its dependency and get the session from the SessionFactory. The first step in creating a Spring Hibernate project is to integrate Hibernate and connect with the database.

Sample data model for example code

In this chapter, we will use a PostgreSQL database. Please refer to http://www.postgresqltutorial.com/install-postgresql/ to set up a PostgreSQL database server on your machine and download the JDBC driver for the PostgreSQL database; we have used the postgresql-9.3-1102.jdbc3.jar JDBC connector for PostgreSQL.

We will create a database named ehrpayroll_db that will contain a table named employee and will populate dummy data to the table. The following is a sample data creation script for a PostgreSQL database.

Let's first create a database for our project in the PostgreSQL database:

  1. Type in the following script to create a database named ehrpayroll_db:
    CREATE DATABASE ehrpayroll_db
  2. Now enter the given script to create a table named EMPLOYEE_INFO:
    CREATE TABLE EMPLOYEE_INFO(
       ID serial NOT NULL Primary key,
       FIRST_NAME varchar(30) not null,
       LAST_NAME varchar(30) not null,
       JOB_TITLE varchar(100) not null,
       DEPARTMENT varchar(100) not null,
       SALARY INTEGER
    );
  3. The next script helps you populate the data for the table employee:
    INSERT INTO EMPLOYEE_INFO
    (FIRST_NAME, LAST_NAME, JOB_TITLE, DEPARTMENT, SALARY)
    VALUES
    ('RAVI', 'SONI', 'AUTHOR', 'TECHNOLOGY', 5000);

The following figure shows the created table with the data inserted:

Sample data model for example code

Integrating Hibernate

To integrate Hibernate, we need to perform these steps:

  1. Download the Hibernate JAR and include them into the classpath. Download (.zip file for Windows) the latest version of Hibernate from http://www.hibernate.org/downloads. Once you unzip the downloaded ZIP file, the directory structure will appear as shown in the following screenshot:
    Integrating Hibernate
  2. Inside the lib directory, there will be a lot of directories that contain Hibernate-related JARs, as shown in the following screenshot. The required folder contains all the JARs you need to create a basic Java application.
    Integrating Hibernate

Once you have downloaded the Hibernate libraries, you can create a new Spring project and add Hibernate libraries to this project using Java Build Path.

Required JARs for the Spring-Hibernate project

We need to add the JARs required to create our Spring-Hibernate projects. These are shown in the following screenshot:

Required JARs for the Spring-Hibernate project

Configuring Hibernate SessionFactory in Spring

The Spring Framework lets us define resources such as JDBC DataSource or Hibernate SessionFactory as a Spring bean in an application context, which prevents the need for hardcoded resource lookups for application objects. This defined Spring bean references are used by application objects that need to access resources to receive the predefined instances.

The Session interface in the Hibernate API provides methods to find, save, and delete objects in a relational database. The Hibernate session is created by first creating the SessionFactory. The Spring Framework provides a number of classes to configure Hibernate SessionFactory as a Spring bean containing the desired properties.

For session creation, the Spring API provides the implementation of the AbstractSessionFactoryBean subclass: the LocalSessionFactoryBean class and the AnnotationSessionFactoryBean class. Since we will be using annotation style, we will use the AnnotationSessionFactoryBean class, which supports annotation metadata for mappings. AnnotationSessionFactoryBean extends the LocalSessionFactoryBean class, so it has all the basic properties of Hibernate integration.

In the configuration file, we have to declare the sessionFactory bean and set dataSource, packagesToScan or annotatedClasses, and hibernateProperties. Let's take a look at these in detail:

  • The dataSource property sets the name of the data source to be accessed by the underlying application.
  • The packagesToScan property instructs Hibernate to scan the domain object with the ORM annotation under the specified package. The annotatedClasses property instructs Hibernate to for the ORM-annotated class.
  • The hibernateProperties property sets the configuration details for Hibernate. We have defined only a few important properties out of many configuration parameters that should be provided for every application.

The following table describes these properties:

Property

Description

hibernate.dialect

Hibernate uses this property to generate the appropriate SQL optimized for the chosen relational database. Hibernate supports SQL dialects for many databases, and the major dialects include PostgreSQLDialect, MySQLDialect, H2Dialect, Oracle10gDialect, and so on.

hibernate.max_fetch_depth

This property is used to set the maximum depth for the outer join when the mapping object is associated with other mapped objects. This property is used to determine the number of associations Hibernate will traverse by join when fetching data. The recommended value lies between 0 and 3.

hibernate.jdbc.fetch_size

This property is used to set the total number of rows that can be retrieved by each JDBC fetch.

hibernate.show_sql

This property file is used to output all SQL to the log file or console, which is an alternative to set log to debug and troubleshooting process. It can be set to either True or False.

Refer to the Hibernate reference manual for the full list of Hibernate properties at http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/session-configuration.html.

XML Spring configuration for Hibernate

Spring beans, the data source, a SessionFactory, and a transaction manager bean are configured in the app-context.xml file. You should adapt your Hibernate beans according to the project requirements.

Here is an implementation of app-context.xml. In the following configuration file, we have declared several beans to support the Hibernate SessionFactory:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
   xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
   xsi:schemaLocation="
          http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd
          http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
          http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
          http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

In the following code snippet, we have instructed Spring to scan the component under the package org.packt.spring.chapter6.hibernate using component-scan:

   <context:annotation-config />
   <context:component-scan base-package="org.packt.spring.chapter6.hibernate" />

The property-placeholder will refer to the hibernate.properties file, as shown in the following code snippet:

   <context:property-placeholder
         location="classpath:/META-INF/spring/hibernate.properties" />

In the following code snippet, the dataSource bean is declared to provide database connection details to Hibernate:

   <bean id="dataSource"
   class="org.springframework.jdbc.datasource.DriverManagerDataSource">
          <property name="driverClassName" value="${jdbc.driverClassName}" />
          <property name="url" value="${jdbc.url}" />
          <property name="username" value="${jdbc.username}" />
          <property name="password" value="${jdbc.password}" />
   </bean>

The sessionFactory bean is declared in the following code snippet. The Hibernate sessionFactory bean is the most important part. We have used AnnotationSessionFactoryBean to support the Hibernate annotation. We have injected the dataSource bean into sessionFactory. We have instructed Hibernate to scan for the ORM annotated object. And then we have provided the configuration details for Hibernate using hibernateProperties, as shown here:

   <bean id="sessionFactory"
   class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
          <property name="dataSource" ref="dataSource" />
          <property name="annotatedClasses"
   value="org.packt.spring.chapter6.hibernate.model.Employee" />
          <property name="hibernateProperties">
               <props>
                     <prop key="hibernate.dialect">${hibernate.dialect}</prop>
                      <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
                </props>
          </property>
   </bean>

In the following code snippet, we have declared the transactionManager bean. To access transactional data, SessionFactory requires a transaction manager. The transaction manager provided by Spring specifically for Hibernate 3 is org.springframework.orm.hibernate3.HibernateTransactionManager:

   <bean id="transactionManager"
   class="org.springframework.orm.hibernate3.HibernateTransactionManager">
         <property name="sessionFactory" ref="sessionFactory" />
   </bean>

The <tx:annotation-driven> is declared in the following code snippet to support transaction demarcation requirements using annotations:

   <tx:annotation-driven transaction-manager="transactionManager" />
</beans>

hibernate.properties

The Hibernate- and JDBC-specific properties are stored in a hibernate.properties file, as follows:

# JDBC Properties
jdbc.driverClassName=org.postgresql.Driver
jdbc.url=jdbc:postgresql://localhost:5432/ehrpayroll_db
jdbc.username=postgres
jdbc.password=sa
  
# Hibernate Properties
hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
hibernate.show_sql=true

Annotated domain model class

The Java persistent model establishes the static relationships of the persistence model by defining the entity component. The API defines the entity class as the object tier of a table in the database tier. An entity instance is defined as the object tier equivalent of a row in a database table.

The following is a table that maps Object Tier elements to Database Tier elements:

Object Tier element

Database Tier element

Entity class

Database table

Field of entity class

Database table column

Entity instance

Database table row

Hibernate annotation provides the metadata for object and relational table mapping. This metadata is clubbed into a POJO file that helps users understand the code inside POJO as well as the table structure simultaneously while developing. Hibernate provides JPA implementation, which allows the user to use JPA annotation in model beans. The JPA annotations are explained in the following table:

JPA annotation

Description

@Entity

The javax.persistence.Entity annotation declares a class as an entity bean that can be persisted by Hibernate, since Hibernate provides JPA implementation.

@Table

The javax.persistence.Table annotation can be used to define table mapping. It provides four attributes that allows us to override the table name, its catalogue, and its schema.

@Id

The javax.persistence.Id annotation is used to define the primary key, and it will automatically determine the appropriate primary key generation strategy to be used.

@GeneratedValue

  • The javax.persistence.GeneratedValue annotation is used to define that the field will be autogenerated
  • It takes two parameters, strategy and generator
  • The GenerationType.IDENTITY strategy is used so that the generated id value is mapped to the bean and can be retrieved by the Java program

@Column

  • The javax.persistence.Column annotation is used to map the field with the table column
  • We can also specify length, nullable, and uniqueness for the bean properties

Now it's time to write Employee.java. This class will be mapped to the Employee table in the database using Hibernate. The Employee class fields are annotated with JPA annotations so that we don't need to provide mapping in a separate XML file. It should be noted that Hibernate puts emphasis on overriding the equals() and hashCode() methods of a persistent class when used in collections (such as a list or set) because internally Hibernate works with the objects in the session and cache. It is recommended to implement the equals() and hashCode() methods using a real-world key, which is a key that would identify the instance in the real world, as shown here:

package org.packt.spring.chapter6.hibernate.model;

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 = "EMPLOYEE_INFO")
public class Employee  {

   @Id
   @Column(name = "ID")
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   private Integer id;

   @Column(name = "FIRST_NAME")
   private String firstName;

   @Column(name = "LAST_NAME")
   private String lastName;

   @Column(name = "JOB_TITLE")
   private String jobTitle;

   @Column(name = "DEPARTMENT")
   private String department;

   @Column(name = "SALARY")
   private int salary;

   // constructor and setter and getter

   @Override
   public boolean equals(Object obj) {
         if (this == obj) {
                return true;
          }
          if (!(obj instanceof Employee)) {
                return false;
          }
          Employee employee = (Employee) obj;
          if (firstName != null ? !firstName.equals(employee.firstName)
                      : employee.firstName != null) {
                return false;
          } else {
                return true;
          }
   }

   @Override
   public int hashCode() {
          return firstName != null ? firstName.hashCode() : 0;
   }

   @Override
   public String toString() {
         return "Employee [id=" + id + ", name=" + firstName + " " + lastName
                       + ", jobTitle=" + jobTitle + " department=" + department
                       + " salary=" + salary + "]";
   }
}

In the preceding code snippet:

  • The Employee class is annotated with the @Entity annotation, which will define this class as a mapped entity class. The Employee class is also annotated with the @Table annotation that defines the table name in the database with which this entity class will map.
  • The ID is annotated with the @ID annotation, which represents that ID is the primary key of the object. Hibernate will generate the ID value based on the @GeneratedValue annotation. The GenerationType.IDENTITY strategy reflects that the ID will be generated by the backend (the ID column of the EMPLOYEE_INFO table is the primary key with SERIAL specified, which means the value of ID will be generated and assigned by the database during the insert operation) during insert.
  • The name and e-mail are annotated with the @Column annotation.
  • If the type and attribute names are exactly the same as the name of table and column, then we can skip the name of the table and column from the annotation.

The Hibernate sessions

The Session interface is an important interface that is required while interacting with a database in Hibernate. The Session interface is obtained from SessionFactory. The Session object is light weight and can be used to attain a physical connection with a database. It is initiated each time an interaction needs to happen with a database. Also, persistent objects are saved and retrieved through a Session object. It is not usually thread safe, so avoid keeping it open for a long time. They should be created and destroyed as needed. The Session interface offers create, delete, and read operations for instances of mapped entity classes.

Instances may exist in one of the following three states at a given point in time:

  • Transient: This state represents a new instance of persistence class that has no representation in a database and is not associated with Session.
  • Persistent: This state represents that the instance of a persistence class has a representation in the database.
  • Detached: In this state, the persistent object will be detached from the database. This state will be reached once the Hibernate Session will be closed.

The Session interface methods

The Session interface provides a numbers of method such as beginTransaction(), createCriteria(), save(), delete(), and so on, which you can read about at http://www.tutorialspoint.com/hibernate/hibernate_sessions.htm.

Persistence layer – implement DAOs

The persistence layer will have the DAO. Let's create DAO classes that will interact with the database using the Hibernate SessionFactory. The SessionFactory implementation will be injected into the reference variable at runtime using Spring's Inversion of Control (IoC) feature.

The EmployeeDao interface

The EmployeeDao interface declares two methods named getAllEmployees() and insertEmployee(), as shown here:

package org.packt.spring.chapter6.hibernate.dao;

import java.util.List;
import org.packt.spring.chapter6.hibernate.model.Employee;

public interface EmployeeDao {

   // to get all employees
   public List<Employee> getAllEmployees();

   // to insert new employee
   public void insertEmployee(Employee employee);
}

The EmployeeDaoImpl class

The EmployeeDaoImpl class is annotated with @Repository, which indicates that this class is a DAO. It also has the @Transactional(readOnly = true) annotation, which configures this class and all its methods for read-only access:

package org.packt.spring.chapter6.hibernate.dao;

import java.util.List;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.packt.spring.chapter6.hibernate.model.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

@Repository
@Transactional(readOnly = true)
public class EmployeeDaoImpl implements EmployeeDao {

   @Autowired
   private SessionFactory sessionFactory;

   @SuppressWarnings("unchecked")
   public List<Employee> getAllEmployees() {
         Session session = sessionFactory.openSession();
         String hql = "FROM Employee";
         Query query = session.createQuery(hql);
         List<Employee> emList = query.list();
         return emList;
   }

   @Transactional(readOnly = false)
   public void insertEmployee(Employee employee) {
         Session session = sessionFactory.openSession();
         session.save(employee);
   }
}

To get a SessionFactory, we declare a member variable named sessionFactory of type SessionFactory and annotated with the @Autowired annotation that automatically initializes the SessionFactory. The next step is to get the session from the sessionFactory.

In order to use Hibernate in the getAllEmployees() and insertEmployees() method, we use a session object. The session object is obtained from a SessionFactory. Using this session object, we can use the createQuery method to create queries and run them.

When we are finished with the session, we should close it. We needn't close it by ourselves; the Spring Framework will do this for us.

Let's understand the methods defined in EmployeeDaoImpl class in more detail:

  • Querying the database – getAllEmployees(): The method getAllEmployees() will fetch all the employee details from the Employee table in the database and return the list of employees.

    This method will get Hibernate Session (Session is the main runtime interface between Java application and Hibernate) from sessionFactory using the openSession() method of the SessionFactory class. This session will use the query object to call the list() method that will fetch employees.

  • Inserting new record – insertEmployee(): The method insertEmployee() will insert a new record to the Employee table. This method will use the save() method defined in the Hibernate Session to perform the INSERT operation.

    Annotate this method with @Transactional(readOnly = false), which will allow us to perform the INSERT operation.

Service layer – implement services

We have defined the Service layer, which seems redundant in this demo due to the lack of complexity. This layer will simply take a call from the controller (in Spring MVC) or from the main method and pass this call to the DAOs layer.

The EmployeeService interface

The EmployeeService interface declares two methods named getAllEmployees() and insertEmployee(), as shown here:

package org.packt.spring.chapter6.hibernate.service;

import java.util.List;
import org.packt.spring.chapter6.hibernate.model.Employee;

public interface EmployeeService {
public List<Employee> getAllEmployees();
public void insertEmployee(Employee employee);
}

The EmployeeServiceImpl class

The EmployeeServiceImpl class will implement the EmployeeService interface and provide a definition for the methods declared in the interface. This class has declared a member variable named EmployeeDAO and annotated it with the @Autowired annotation. This class is annotated with the @Service annotation, which makes this class a service class:

package org.packt.spring.chapter6.hibernate.service;

import java.util.List;

import org.packt.spring.chapter6.hibernate.dao.EmployeeDao;
import org.packt.spring.chapter6.hibernate.model.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class EmployeeServiceImpl implements EmployeeService {

   @Autowired
   private EmployeeDao employeeDao;

   public List<Employee> getAllEmployees() {
          List<Employee> emList = employeeDao.getAllEmployees();
          return emList;
   }

   public void insertEmployee(Employee employee) {
          employeeDao.insertEmployee(employee);
   }
}

Directory structure of the application

The final directory structure of the application is as follows:

Directory structure of the application

Running the application

Once we are done with the preceding configuration, we can write a main method to store values from the Employee object to the database.

The DBUtils class

We have created a DBUtils class annotated with the @Component annotation to register this class to the Spring container as a bean. This class defined a method named initialize() and annotated it with the @PostConstruct annotation.

The @PostConstruct annotation does not belong to Spring, it's located in the J2EE library: common-annotations.jar. The @PostConstruct annotation is a shared annotation that is part of a JSR for basic annotations. It comes with Java SE 6 or newer versions. The commons-annotations.jar is the final product of the JSR API. The @PostConstruct annotation defines a method that will be called after a bean has been fully initialized. In other words, it will be called after bean construction and the injection of all dependencies.

The initialize() method will get the database connection and create a table EMPLOYEE and insert dummy data to this table. This class has been used in this project to prevent exceptions in case we miss out on creating a table in the database. In a real-world application, we don't need this class:

package org.packt.spring.chapter6.hibernate.util;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;

import javax.annotation.PostConstruct;
import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class DBUtils {

   @Autowired
   private DataSource dataSource;

   @PostConstruct
   public void initialize() {
         try {
                Connection connection = dataSource.getConnection();
                Statement statement = connection.createStatement();

                statement.execute("DROP TABLE IF EXISTS EMPLOYEE_INFO");

                statement.executeUpdate("CREATE TABLE EMPLOYEE_INFO(" +
                       "ID serial NOT NULL Primary key, " +
                       "FIRST_NAME varchar(30) not null, " +
                       "LAST_NAME varchar(30) not null, " +
                       "JOB_TITLE varchar(100) not null, " +
                       "DEPARTMENT varchar(100) not null, " +
                       "SALARY INTEGER)";

                statement.executeUpdate("INSERT INTO EMPLOYEE_INFO "
                       + "(FIRST_NAME, LAST_NAME, JOB_TITLE, DEPARTMENT, SALARY) "
                       + "VALUES " + "('RAVI', 'SONI', 'AUTHOR', 'TECHNOLOGY', 5000)";

                statement.close();
                connection.close();
          } catch (SQLException e) {
                e.printStackTrace();
          }
   }
}

The SpringHibernateMain class

The SpringHibernateMain class contains the main method. The ApplicationContext will initialize the container with the app-context.xml file we defined:

package org.packt.spring;

import org.packt.spring.chapter6.hibernate.model.Employee;
import org.packt.spring.chapter6.hibernate.service.EmployeeService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringHibernateMain {

   public static void main(String[] args) {

          ApplicationContext context = new ClassPathXmlApplicationContext(
                      "/META-INF/spring/app-context.xml");

          EmployeeService employeeService = context.getBean(
                      "employeeServiceImpl", EmployeeService.class);

          // insert employee
          Employee emp = new Employee();
          emp.setFirstName("Shree");
          emp.setLastName("Kant");
          emp.setJobTitle("Software Engineer");
          emp.setDepartment("Technology");
          emp.setSalary(3000);
          employeeService.insertEmployee(emp);

          // fetch all employee
          for (Employee employee : employeeService.getAllEmployees())
                System.out.println(employee);
   }
}

Output to console

Once you run the application, the following output will be expected:

Hibernate: insert into EMPLOYEE_INFO (DEPARTMENT, FIRST_NAME, JOB_TITLE, LAST_NAME, SALARY) values (?, ?, ?, ?, ?)

Hibernate: select employee0_.ID as ID0_, employee0_.DEPARTMENT as DEPARTMENT0_, employee0_.FIRST_NAME as FIRST3_0_, employee0_.JOB_TITLE as JOB4_0_, employee0_.LAST_NAME as LAST5_0_, employee0_.SALARY as SALARY0_ from EMPLOYEE_INFO employee0_

Employee [id=1, name=RAVI SONI, jobTitle=AUTHOR department=TECHNOLOGY salary=5000]

Employee [id=2, name=Shree Kant, jobTitle=Software Engineer department=Technology salary=3000]

Populated data in the Employee table

Once the application has been run successfully, the updated Employee table with all the data will be as shown here:

Populated data in the Employee table

In the previous sections, we discussed mapping persistent objects using Hibernate. In the next section, we will understand HQL. Hibernate is engineered around the object model and provides a powerful query language named HQL to define our queries so we don't need to construct SQL to interact with the database. HQL is similar to SQL except that we will use objects instead of table names.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.149.243.106