An Introduction to Hibernate and Spring: Part 1

Exclusive offer: get 50% off this eBook here
Spring Persistence with Hibernate

Spring Persistence with Hibernate — Save 50%

Build robust and reliable persistence solutions for your enterprise Java application

$29.99    $15.00
by Ahmad Seddighi | November 2009 | Java Open Source

This article by Ahmad Seddighi, introduces Spring and Hibernate, explaining what persistence is, why it is important, and how it is implemented in Java applications. It provides a theoretical discussion of Hibernate and how Hibernate solves problems related to persistence. Finally, we take a look at Spring and the role of Spring in persistence.

Hibernate and Spring are open-source Java frameworks that simplify developing Java/JEE applications from simple, stand-alone applications running on a single JVM, to complex enterprise applications running on full-blown application servers. Hibernate and Spring allow developers to produce scalable, reliable, and effective code. Both frameworks support declarative configuration and work with a POJO (Plain Old Java Object) programming model (discussed later in this article), minimizing the dependence of application code on the frameworks, and making development more productive and portable.

Although the aim of these frameworks partially overlap, for the most part, each is used for a different purpose. The Hibernate framework aims to solve the problems of managing data in Java: those problems which are not fully solved by the Java persistence API, JDBC (Java Database Connectivity), persistence providers, DBMS (Database Management Systems), and their mediator language, SQL (Structured Query Language).

In contrast, Spring is a multitier framework that is not dedicated to a particular area of application architecture. However, Spring does not provide its own solution for issues such as persistence, for which there are already good solutions. Rather, Spring unifies preexisting solutions under its consistent API and makes them easier to use. As mentioned, one of these areas is persistence. Spring can be integrated with a persistence solution, such as Hibernate, to provide an abstraction layer over the persistence technology, and produce more portable, manageable, and effective code.

Furthermore, Spring provides other services spread over the application architecture, such as inversion of control and aspect-oriented programming (explained later in this article), decoupling the application's components, and modularizing common behaviors.

This article looks at the motivation and goals for Hibernate and Spring. The article begins with an explanation of why Hibernate is needed, where it can be used, and what it can do. We'll take a quick look at Hibernates alternatives, exploring their advantages and disadvantages. I'll outline the valuable features that Hibernate offers and explain how it can solve the problems of the traditional approach to Java persistence. The discussion continues with Spring. I'll explain what Spring is, what services it offers, and how it can help to develop a high-quality data-access layer with Hibernate.

Persistence management in Java

Persistence has long been a challenge in the enterprise community. Many persistence solutions from primitive, file-based approaches, to modern, object-oriented databases have been presented. For any of these approaches, the goal is to provide reliable, efficient, flexible, and scalable persistence.

Among these competing solutions, relational databases (because of certain advantages) have been most widely accepted in the IT world. Today, almost all enterprise applications use relational databases. A relational database is an application that provides the persistence service. It provides many persistence features, such as indexing data to provide speedy searches; solves the relevant problems, such as protecting data from unauthorized access; and handles many complications, such as preserving relationships among data. Creating, modifying, and accessing relational databases is fairly simple. All such databases present data in two-dimensional tables and support SQL, which is relatively easy to learn and understand. Moreover, they provide other services, such as transactions and replication. These advantages are enough to ensure the popularity of relational databases.

To provide support for relational databases in Java, the JDBC API was developed. JDBC allows Java applications to connect to relational databases, express their persistence purpose as SQL expressions, and transmit data to and from databases. The following screenshot shows how this works:

Spring Persistence with Hibernate

Using this API, SQL statements can be passed to the database, and the results can be returned to the application, all through a driver.

The mismatch problem

JDBC handles many persistence issues and problems in communicating with relational databases. It also provides the needed functionality for this purpose. However, there remains an unsolved problem in Java applications: Java applications are essentially object-oriented programs, whereas relational databases store data in a relational form. While applications use object-oriented forms of data, databases represent data in two-dimensional table forms. This situation leads to the so-called object-relational paradigm mismatch, which (as we will see later) causes many problems in communication between object-oriented and relational environments.

For many reasons, including ease of understanding, simplicity of use, efficiency, robustness, and even popularity, we may not discard relational databases. However, the mismatch cannot be eliminated in an effortless and straightforward manner.

Spring Persistence with Hibernate Build robust and reliable persistence solutions for your enterprise Java application
Published: November 2009
eBook Price: $29.99
Book Price: $49.99
See more
Select your format and quantity:

Identity and equality mismatch

The first and most significant mismatch involves the concepts of data equality. Java provides two definitions for object identity and equality. According to Java, two objects are called identical when they point to the same reference in memory. In contrast, two objects are considered equal when they contain similar data, as determined by the developer, regardless of the memory locations to which the objects point.

Java offers the equals() method and == operator to support equality and identity, respectively. For instance, the == operator can be used as follows to check whether object1 and object2 are identical:

object1==object2

The equals() method, used as follows, determines whether two objects are equal:

object1.equals(object2)

When two objects are identical, they refer to the same memory location. Therefore, they have the same value and are definitely equal. However, two objects that are equal may not be identical since they may point to different locations in memory.

The hashCode() method must be overridden in every class which overrides the equals() method. The hashCode() method returns an integer as the hash code value for the object on which this method is invoked. This code is supported for the benefit of hashing based collection classes such as Hashtable, HashMap, HashSet, and so on. Equal objects must produce the same hash code, as long as they are equal. However, unequal objects do not need to produce distinct hash codes. See the JDK documentation for more details

While Java offers two distinct definitions for object identity and equality, databases do not have any corresponding definitions for these terms. In a database, the data is represented as table rows, and each table row is identified based on the content it holds. A significant mismatch occurs when we map from the object-oriented world to the relational world. Although two objects are not identical because they refer to different locations in memory, in the database, they may be considered identical because they hold the same content.

The common approach to eliminating this mismatch is to use an extra field in the object's class, and an extra identifier column in the respective table of the object. This approach identifies objects based on the identifier values they hold in either object or relational form, instead of identifying them based on their references in memory (in the object-oriented world) and based on the content they hold (and in the relational world). Therefore, as the following screenshot shows, each object and its corresponding row in the table can be identified through the same strategy, that is, by considering the identifier value:

Spring Persistence with Hibernate

Let's look at a simple example. Suppose that our application has a Student class. We may typically use a STUDENT table in the database to store Student objects. We may also use an extra field in the class, called an object identifier, and a corresponding column in the table, called primary key, to allow objects to be recognized when they are stored, retrieved, or updated. The following screenshot shows the STUDENT table:

Spring Persistence with Hibernate

This table would hold the objects of the class shown in the following code listing. Note that, we have just shown the skeleton of the class with its properties and without its other details:

package com.packtpub.springhibernate.ch01;

import java.util.Date;

public class Student {
private int id;
private String firstName;
private String lastName;
private String ssn;
private Date birthdate;
private String stdNo;
private Date entranceDate ;


}

We may also use the following JDBC code in our application to store a Student object in the STUDENT table:

Connection c = null;
PreparedStatement p = null;

try {
Student std = ...//a ready to store Student object
c = ...//obtaining a Connection object
String q = "INSERT INTO STUDENT " +
"(ID, FIRST_NAME, LAST_NAME, SSN, BIRTHDATE, STD_NO, ENTRANCE_DATE)" +
" VALUES (?, ?, ?, ?, ?, ?, ?)";
p = c.prepareStatement(q);
p.setInt(1, std.getId());
p.setString(2, std.getFirstName());
p.setString(3, std.getLastName());
p.setString(4, std.getSsn());
p.setDate(5, new java.sql.Date(std.getBirthdate().getTime()));
p.setString(6, std.getSsn());
p.setDate(7, new java.sql.Date(std.getEntranceDate().getTime()));

p.executeUpdate();
} catch(Exception ex) {
//handling the exception
} finally {
if (p != null) {
try {
p.close();
} catch (SQLException e) {
//handling the exception
}
}
if (c != null) {
try {
c.close();
} catch (SQLException e) {
//handling the exception
}
}
}

As you can see, to store a Student object, we need to obtain a PreparedStatement object with an appropriate SQL query. We then need to put the properties of the Student object in the PreparedStatement, and finally execute the update query by calling the executeUpdate() method of PreparedStatement. With all of these, we should handle exceptions when there is any problem in communicating to the database or executing the query.

Similarly, we may use code such as the following to load a Student object

Connection c = null;
PreparedStatement p = null;
Student std = null;

try {
int id = ...//the identifier of the student to load
c = ...//obtaining a Connection object
String q = "SELECT FIRST_NAME, LAST_NAME, SSN, BIRTHDATE,
STD_NO, ENTRANCE_DATE " + "FROM STUDENT WHERE ID = ?";
p = c.prepareStatement(q);
p.setInt(1, id);
ResultSet rs = p.executeQuery();
if (rs.next()) {
String firstName = rs.getString("FIRST_NAME");
String lastName = rs.getString("LAST_NAME");
String ssn = rs.getString("SSN");
Date birthdate = rs.getDate("BIRTHDATE");
String stdNo = rs.getString("STD_NO");
Date entranceDate = rs.getDate("ENTRANCE_DATE");
std = new Student(id, firstName, lastName, ssn, birthdate, stdNo, entranceDate);
if (rs.next()) {
//write a message warning about multiple objects
}
}
} catch(Exception ex) {
//handling the exception
} finally {
if (p != null) {
try {
p.close();
} catch (SQLException e) {
//handling the exception
}
}
if (c != null) {
try {
c.close();
} catch (SQLException e) {
//handling the exception
}
}
}
return std;

To load an object from the database, we need to obtain a PreparedStatment with an appropriate SQL query, set the required values, execute the query, iterate over the result, and produce the Student object. We should also handle the exceptions correspondingly.

The most difficult and important parts of the above codes are the SQL statements. These statements are expressed in a different language than the language that object-oriented languages such as Java use. Besides, their syntax can't be checked in the compiled time since they are expressed in the raw String.

As it can be concluded from both the previous examples, converting the object-oriented and relational forms of data to one another cannot be accomplished in an effortless and straightforward manner. After all, any change in the object model or the database schema can affect the converting code.

This example demonstrates a primary difficulty in mapping objective and relational forms of data. However, this simple case requires only minimal effort to map a typical entity object. The mapping of objects could easily be more complicated than in the example, especially when an entity object is inherited from or associated with another object.

Mapping object inheritance mismatch

Another point where a mismatch occurs is in the mapping of object inheritance. While Java lets an object be inherited by another object, relational databases do not support the notion of inheritance. This means we need to define our own strategy to translate class hierarchy to database schema.

Let's elaborate inheritance by extending our simple example using a general class, Person, as a superclass of Student. The class diagram shown in the following screenshot shows the Student class, which is now a subclass of Person:

Spring Persistence with Hibernate

The question here is, how can the object inheritance be persisted? Can it be persisted in one table? If not, how should we establish the relationship between tables?

One solution would be to use individual tables for individual classes in the hierarchy. According to this solution, the objects of type superclass are stored directly in the superclass's table, but the subclass objects are persisted in both superclass and subclass tables. If we chose this strategy for our example, we would have two tables, PERSON and STUDENT, as shown in the following figure:

Spring Persistence with Hibernate

Because objects of the subclass are spread over two tables, we need a mechanism to recognize the association between rows when an object is retrieved, updated, or removed. Fortunately, many databases support foreign keys which are used to establish a relationship between database tables. This is what I have used in our example. As you can see, the STUDENT table takes the ID column as its primary key and a foreign key onto the PERSON table, meaning it holds the identifier of the associated row in the STUDENT table . When a Student object is stored, updated, or removed, the relevant student's data should be inserted in, updated in, or removed from the two tables. Moreover, to load a Student object, we need to query both tables with SQL joins, like this:

Connection c = null;
PreparedStatement p = null;
Student std = null;
try {
int id =...//the identifier of the student that is loaded
c = ...//obtaining a Connection object
c.setAutoCommit(false);
String q = "SELECT P.FIRST_NAME, P.LAST_NAME, P.SSN, " +
"P.BIRTHDATE, S.STD_NO,
S.ENTRANCE_DATE "+
"FROM PERSON P INNER JOIN STUDENT S " +
" ON P.ID = S.PERSON_ID WHERE P.ID=?; ";
p = c.prepareStatement(q);
p.setInt(1, id);
ResultSet rs = p.executeQuery();
if (rs.next()) {
String firstName = rs.getString("FIRST_NAME");
String lastName = rs.getString("LAST_NAME");
String ssn = rs.getString("SSN");
Date birthday = rs.getDate("BIRTHDATE");
String stdNo = rs.getString("STD_NO");
Date entranceDate = rs.getDate("ENTRANCE_DATE");
std = new Student(id, firstName, lastName, ssn,
birthday, stdNo, entranceDate);
if (rs.next()) {
//making a message warning about multiple objects existence
}
}
p.close();
c.commit();
} catch (Exception ex) {
//handling the exception
} finally {
if (p != null) {
try {
p.close();
} catch (SQLException e) {
//handling the exception
}
}
if (c != null) {
try {
c.close();
} catch (SQLException e) {
//handling the exception
}
}
}
return std;

As you can see, using SQL joins makes the query expressions more complex, and consequently harder to develop, test, and maintain.

Mapping more complicated objects

Many Java objects are associated with other objects. The associated objects may be values or entities. Entities are objects that have their own persistent identity and are stored as discussed before. In contrast, values are objects that do not define some kind of persistent identity. If an entity object is associated with a value object, no real problem arises since the value object can be stored with the entity object in the same table. However, this is not true in the case of associations between two entity objects. Unfortunately, databases do not offer a way to persist object associations by default.

The next mismatch happens when a graph of entity objects must be persisted in the database. In this case, the persistence should be accomplished in such a way that allows the object graph to be restored to its original form at a later time. As a common strategy, for each entity class a database table is used and when an object is stored, each object is persisted in its own database table. The next question here is, how can object associations be persisted? As with inheritance, foreign keys can be taken to establish inter-table relationships and provide table associations.

Let's dig a bit deeper into object associations and extend our example to see how JDBC and SQL deal with associations in practice.

Mapping a many-to-many association

Let's assume that each student is associated with an array of courses. If each course is represented by another class, Course, then we have an object relationship like the one shown in the following screenshot:

Spring Persistence with Hibernate

The following code shows the Course class:

package com.packtpub.springhibernate.ch01;

public class Course {
private int id;
private String courseName;
private int units;

//setter and getter methods
}

And this shows the Student class:

package com.packtpub.springhibernate.ch01;

import java.util.Date;
import java.util.List;

public class Student extends Person {
private String stdNo;
private Date entranceDate;
private List courses;

//setter and getter methods
}

When we work with Hibernate, we always use simple classes which do not have any special behaviors. It is recommended that these classes be expressed with private properties and with setter and getter methods to access the properties.

Note that, we have designed our classes as simple to be as possible to keep our example simple, as well. In this case, the COURSE table needs a foreign key column referring to the associated row in the STUDENT table. The tables may be related as shown in the following screenshot:

Spring Persistence with Hibernate

This kind of relationship indicates that when a Student object is persisted, the associated Course objects should be persisted as well. However, we may use a different strategy for updating or removing associated objects when the object is updated or removed. For example, we may decide that the associated objects are not to be updated when the object is updated, but they should be erased from the database when the object is removed. This is what we call a cascade operation, indicating whether the operation, or operations, should be propagated to associated entities.

To load an entity object, we must query the appropriate table and any others associated with it. The following snippet shows the query to load a Student object with its associated courses:

SELECT PERSON.FIRST_NAME, PERSON.LAST_NAME, PERSON.SSN,
PERSON.BIRTHDATE, STUDENT.STD_NO, STUDENT.ENTRANCE_DATE,
COURSE.ID, COURSE.COURSE_NAME, COURSE.UNITS FROM PERSON INNER
JOIN (STUDENT INNER JOIN COURSE ON STUDENT.ID =
COURSE.STUDENT_ID) ON PERSON.ID = STUDENT.PERSON_ID WHERE
STUDENT.ID=?;

It is very difficult, tedious, and error prone to use only pure JDBC and SQL to store the object graph in multiple tables, restore the object-oriented form of data, search object associations, and handle object inheritance. The SQL statements you use may not be optimized, and may be very difficult to test and maintain.

This is the main reason to use a persistence framework such as Hibernate. The next section looks at Hibernate's background, as well as its advantages, alternatives, and architecture.

>> Continue Reading: An Introduction to Hibernate and Spring - Part 2

 

If you have read this article you may be interested to view :

Spring Persistence with Hibernate Build robust and reliable persistence solutions for your enterprise Java application
Published: November 2009
eBook Price: $29.99
Book Price: $49.99
See more
Select your format and quantity:

About the Author :


Ahmad Seddighi

Ahmad Reza Seddighi is an author, speaker, and consultant in architecting and developing enterprise software systems. He is an IT graduate of the University of Isfahan, Iran, and has ten years of experience in software development. Currently, he lives in Tehran, where he works with a number of small but growing IT companies. He is also the author of three other books: Core Java Programming, Java Web Development, and Open Source J2EE Development, all in Farsi.

Books From Packt


JSF 1.2 Components
JSF 1.2 Components

JBoss RichFaces 3.3
JBoss RichFaces 3.3

Papervision3D Essentials
Papervision3D Essentials

RESTful Java Web Services
RESTful Java Web Services

jQuery 1.3 with PHP
jQuery 1.3 with PHP

Tomcat 6 Developer's Guide
Tomcat 6 Developer's Guide

JBoss AS 5 Development
JBoss AS 5 Development

Pentaho Reporting 3.5 for Java Developers
Pentaho Reporting 3.5 for Java Developers


Your rating: None Average: 2 (1 vote)

Post new comment

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
m
E
R
H
z
e
Enter the code without spaces and pay attention to upper/lower case.
Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software