Spring Persistence with Hibernate

By Ahmad Seddighi
  • Instant online access to over 7,500+ books and videos
  • Constantly updated with 100+ new titles each month
  • Breadth and depth in over 1,000+ technologies
  1. An Introduction to Hibernate and Spring

About this book

Spring is the leading platform to build and run enterprise Java applications. Spring's Hibernate integration makes it easy to mix and match persistence methodologies simplifying your Hibernate applications. You can incorporate lots of Inversion of Control (IoC) convenience features to address many typical Hibernate integration issues, making this integration all the more favorable for your application.

This easy-to-use book will turn the complex-sounding integration into a straightforward walk-through. Persistence is important for creating a data access-based transactions tier, central to financial, insurance, and banking applications. You will be able to enhance your applications using the most common, advanced, and optional features of Hibernate.

This book starts with the philosophy and the brief history of persistence. It provides an introduction to how persistence frameworks and technologies came into the development scene and what problems they are aimed to solve.

The book continues with a discussion about Hibernate as the most popular persistence framework in Java. First, you will learn how to get Hibernate and add it to a project and how to configure it before it can be used. Next, you will get an in-depth knowledge about Hibernate and understand the essential concepts behind persistence with Hibernate and more. When Hibernate has been fully discussed, you will get to know Spring as another popular framework in Java, and have a look at essential features of Spring and its added value for Hibernate-based projects. Finally the book will provide a comprehensive discussion about using Hibernate with Spring and the problems that are solved with Spring.

Publication date:
November 2009
Publisher
Packt
Pages
460
ISBN
9781849510561

 

Chapter 1. An Introduction to Hibernate and Spring

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 chapter), 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 chapter), decoupling the application's components, and modularizing common behaviors.

This chapter looks at the motivation and goals for Hibernate and Spring. The chapter 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.

If you already have enough background to start learning Hibernate and Spring, you can skip this chapter and jump to the next one.

 

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:

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.

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.

Note

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:

Let's look at a simple example. Suppose that our application has a Student class. We may typically use a ST UDENT 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:

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.

Note

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. Here, we are not going to discuss issues behind persisting inheritance and such in detail. However, a quick look at how pure JDBC deals with them offers insight into how Hibernate can ease object mapping, as we'll see in this book.

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:

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:

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:

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
}

Note

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. This way of class definition has many advantages that will be fully discussed in the coming chapters.

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:

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.

 

Object relational mapping


As the previous discussion shows, we are looking for a solution that enables applications to work with the object representation of the data in database tables, rather than dealing directly with that data. This approach isolates the business logic from any relational issues that might arise in the persistence layer. The strategy to carry out this isolation is generally called object/relational mapping (O/R Mapping, or simply ORM).

A broad range of ORM solutions have been developed. At the basic level, each ORM framework maps entity objects to JDBC statement parameters when the objects are persisted, and maps the JDBC query results back to the object representation when they are retrieved. Developers typically implement this framework approach when they use pure JDBC. Furthermore, ORM frameworks often provide more sophisticated object mappings, such as the mapping of inheritance hierarchy and object association, lazy loading, and caching of the persistent objects. Caching enables ORM frameworks to hold repeatedly fetched data in memory, instead of being fetched from the database in the next requests, causing deficiencies and delayed responses, the objects are returned to the application from memory. Lazy loading, another great feature of ORM frameworks, allows an object to be loaded without initializing its associated objects until these objects are accessed.

ORM frameworks usually use mapping definitions, such as metadata, XML files, or Java annotations, to determine how each class and its persistent fields should be mapped onto database tables and columns. These frameworks are usually configured declaratively, which allows the production of more flexible code.

Many ORM solutions provide an object query language, which allows querying the persistent objects in an object-oriented form, rather than working directly with tables and columns through SQL. This behavior allows the application to be more isolated from the database properties.

 

Hibernate as an O/R Mapping solution


For a long time, Hibernate has been the most popular persistence framework in the Java community. Hibernate aims to overcome the already mentioned impedance mismatch between object-oriented applications and relational databases.

With Hibernate, we can treat the database as an object-oriented store, thereby eliminating mapping of the object-oriented and relational environments. Hibernate is a mediator that connects the object-oriented environment to the relational environment. It provides persistence services for an application by performing all of the required operations in the communication between the object-oriented and relational environments. Storing, updating, removing, and loading can be done regardless of the objects persistent form. In addition, Hibernate increases the application's effectiveness and performance, makes the code less verbose, and allows the code to be more focused on business rules than persistence logic. The following screenshot depicts Hibernates role in persistence:

Hibernate fully supports object orientation, meaning all aspects of objects, such as association and inheritance, are properly persisted. Hibernate can also persist object navigation, that is, how an object is navigable through its associated objects. It caches data that is fetched repeatedly and provides lazy loading, which notably enhances database performance. As you will see, Hibernate provides caches in two levels: first-level built-in, and second-level pluggable cache strategies. The first-level cache is a required property for any ORM to preserve object consistency. It guaranties that the application always works with consistent objects. This is originated from the fact that many threads in the application use the ORM to persist the objects which might potentially be associated to the same table rows in the database. The following screenshot depicts the role of a cache when using Hibernate:

Hibernate provides its own query language, which is Hibernate Query Language (HQL). At runtime, HQL expressions are transformed to their corresponding SQL statements, based on the database used. Because databases may use different versions of SQL and may expose different features, Hibernate presents a new concept, called an SQL dialect, to distinguish how databases differ. Furthermore, Hibernate allows SQL expressions to be used either declaratively or programmatically, which is useful in specific situations when Hibernate does not satisfy application persistence requirements.

Hibernate keeps track of object changes through snapshot comparisons to prevent unnecessary updating.

Other O/R Mapping solutions

Although Hibernate is the most popular persistence framework, many other frameworks do exist. Some of these are explained as follows:

  • Enterprise JavaBeans (EJB): It is a standard J2EE (Java 2 Enterprise Edition) technology that defines a different type of persistence by presenting entity beans. Mostly, for declarative middleware services that are provided by the application server, such as transactions, EJB may be preferred for architecture. However, due to its complexity, nontransparent persistence, and need for a container (all of which make it difficult to implement, test, and maintain), EJB is less often used than other persistence frameworks.

  • iBatis SQL Map: It is a result set–mapping framework which works at the SQL level, allowing SQL string definitions with parameter placeholders in XML files. At runtime, the placeholders are filled with runtime values, either from simple parameter objects, JavaBeans properties, or a parameter map. To their advantage, SQL maps allow SQL to be fully customized for a specific database. To their disadvantage, however, these maps do not provide an abstraction from the specific features of the target database.

  • Java Data Objects (JDO): It is a specification for general object persistence in any kind of data store, including relational databases and object-oriented databases. Most JDO implementations support using metadata mapping definitions. JDO provides its own query language, JDOQL, and its own strategy for change detection.

  • TopLink: It provides a visual mapping editor (Mapping Workbench) and offers a particularly wide range of object, relational mappings, including a complete set of direct and relational mappings, object-to-XML mappings, and JAXB (Java API for XML Binding) support. TopLink provides a rich query framework that supports an object-oriented expression framework, EJB QL, SQL, and stored procedures. It can be used in either a JSE or a JEE environment.

Tip

Hibernate designers has borrowed many Hibernate concepts and useful features from its ancestors.

Hibernate versus other frameworks

Unlike the frameworks just mentioned, Hibernate is easy to learn, simple to use, comprehensive, and (unlike EJB) does not need an application server. Hibernate is well documented, and many resources are available for it. Downloaded more than three million times, Hibernate is used in many applications around the world. To use Hibernate, you need only J2SE 1.2 or later, and it can be used in stand-alone or distributed applications.

The current version of Hibernate is 3, but the usage and configuration of this version are very similar to version 2. Most of the changes in Hibernate 3 are compatible with Hibernate 2.

Hibernate solves many of the problems of mapping objects to a relational environment, isolating the application from getting involved in many persistence issues. Keep in mind that Hibernate is not a replacement for JDBC. Rather, it can be thought of as a tool that connects to the database through JDBC and presents an object-oriented, application-level view of the database.

 

Hibernate architecture


The following screenshot depicts the main participants in the Hibernate architecture:

As the screenshot shows, the main players are Hibernate configuration file(s), mapping definitions, and persistent objects. At the heart of Hibernate is its configuration. This configuration is always presented by an XML, or a properties file and includes the relevant database information, such as database username, password, URL, driver class, and SQL dialect that Hibernate needs for connecting to the database, communicating with it, and performing persistence operations.

Persistent objects form another part of the Hibernate architecture. These objects are what we will persist in the database. These entity objects and their classes, as upcoming chapters explain, do not need to exhibit any special behavior, except that they must follow some POJO rules.

In addition to the Hibernate configuration file and the persistent objects, Hibernate's architecture uses other XML documents, which define how application objects should be mapped to database tables. These documents specify the respective table of each entity class, the mapping of each class's field to its respective table column, and sometimes other mapping information, such as object associations and inheritance. These files have a simple syntax, making them easy to develop and maintain. However, some utility tools that ship with Hibernate let you automatically generate the mapping files, based on the application classes or database schema, and also allow you to modify them in a graphic tool.

Although these objects are the main players in the Hibernate architecture, a Hibernate application's runtime architecture is not limited to them. As we will see in the chapters that follow, the most significant runtime objects are Configuration, SessionFactory, Session, and Transaction.

Hibernate can be used as simply as follows to store or retrieve a Student object:

Configuraion cfg = new Configuration();
cfg.configure();
SessionFactory sessionFactory = cfg.buildSessionFactory();
Student student = …//a new instantiated student object
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
session.save(student);
tx.commit();
session.close();

Here, we have just configured Hibernate, started a transaction, stored the student object, committed the transaction, and finally disconnected from Hibernate. These are actually the required operations to interact with Hibernate. The configuration includes setting up Hibernate to work with a particular database with special behavior. Every Hibernate interaction should be done inside a transaction, which justifies the next step. Don't worry, all of these concepts with their usages will be explained in the future chapters.

 

What is Spring


Now that you've read a bit about Hibernate's background, it is time to look at Spring and what it has to offer. Here, we'll take a quick look at Spring, saving the details for later chapters.

Spring is an ambitious framework that aims to be a complete solution for entire JEE applications. Unlike Hibernate, which works merely on the persistence tier, Spring is a multitier framework which offers a wide range of services. It makes JEE development easier, by providing a clean separation of concerns, decoupling an application's components, and minimizing complexities typically encountered in sophisticated JEE environments.

Choosing an appropriate solution for a Java application, particularly when it is built with open-source tools, is a common challenge in application design. This is another challenge that Spring aims to address. The challenge starts when you encounter a large number of open-source technologies that may be used for the same purpose, such as Struts, WebWork, JSF, or Tapestry for web, and Hibernate, JDO, and iBatis for the persistence tier. Spring lets you use a large variety of open-source tools behind the scenes, without needing large amounts of code or coupling the application too closely to the underlying frameworks.

Spring is also called a lightweight framework, since it replaces frameworks that are restrictive and cumbersome to use, such as EJB, that are already offered by JEE.

Spring is modularized with several components. Each component provides a particular service. The following sections summarize some of these.

Inversion of Control container

Inversion of Control (IoC) is the technology most identified with Spring. With the IoC core container, Spring enables the management of object dependencies by pushing dependencies into objects at runtime, instead of letting the objects pull their dependencies from their environment. This approach has many advantages:

  • All application classes are designed as simple as possible with minimum behaviors, and with their only required properties they will be well documented.

  • All application classes are self-documented, and the documentation is always up-to-date.

  • No class has its own configuration management, which allows more manageable code.

  • The application leaves configuration management to the framework.

  • IoC increases consistency in configuration management, since such management is accomplished by the framework.

  • The application has no need for any configuration management code since the framework handles this common aspect of every application.

Chapter 10 discusses Spring's Inversion of Control in depth.

Aspect-oriented programming framework

Aspect-oriented programming (AOP) is the perfect complementary approach to IoC, solving common problems related to J2EE design. AOP allows us to consolidate functionality, which would be otherwise scattered in different places, in a single place. Managing transactions is an example of this functionality. With Spring, transaction management occurs in a single place and is not scattered in persistence methods.

AOP complements object-oriented programming (OOP) by introducing a new concept, called concerns or aspects, to model real-world objects. Concerns are processes that are not directly related to the object hierarchy. Instead, they spread over sets of operations. For example, logging, security, and transaction are common examples for a concern, since they should be applied to sets of methods without any relationship to the object hierarchy.

Spring AOP has the following key benefits:

  • It prevents code duplication and provides more manageable code.

  • It allows declarative enabling or disabling concerns.

Aspect-oriented programming with Spring is fully discussed in Chapter 11.

Data access abstraction

Spring allows consistent data access to be implemented with solid abstraction. This is carried out through a rich hierarchy of exceptions and a set of helper classes, for working with a wide range of persistence technologies.

We'll look at Spring's Data Access Abstraction and how Spring is integrated with Hibernate in Chapter 13.

Transaction abstraction

Spring provides a transaction abstraction layer over JTA (Java Transaction API) global transactions, which span multiple transactional resources. They are managed by an application server, or local transactions managed by JDBC, Hibernate, JDO, or any other persistence technology. It allows coding transactions, either programmatically or declaratively. These topics are discussed in Chapter 12.

MVC web framework

Spring provides a rich, powerful web framework based on the Model-View-Controller (MVC) pattern. The aim of the framework, like any other web framework, is to simplify web development. It allows a variety of different view technologies, such as JavaServer Pages, Velocity, and iText PDF to be used. You can use MVC with Spring's other services, such as AOP and IoC. Furthermore, Spring can be integrated with other web frameworks, including Struts, WebWork, Tapestry, and JSF. Discussing Spring's web framework is beyond this book's scope. However, we will take a quick look at it, along with its alternatives, to give you a sense of how, in practice, Spring can be used to make web development easier and solve typical problems related to web applications.

Chapter 14 explains in detail the different strategies Spring offers for supporting web frameworks.

Testing support

Spring applications are more readily testable than other applications. This is because Spring applications rely on POJOs, which do not call any Spring APIs, and their dependencies are normally expressed in the form of interfaces that are easy to stub or mock. Moreover, Spring provides some useful helper classes for implementing test classes that test an application's interaction with Spring.

Moreover, Spring provides a lightweight container against traditional full-blown JEE containers. The container provided by Spring can easily be started from the JUnit test itself, which is not easy to do with EJB-3 and a JEE container.

Chapter 15 looks at using Spring's testing abilities to test Hibernate and Spring applications.

 

Summary


This chapter looked at Hibernate's background and explored how the framework provides a bridge between the object-oriented and relational worlds. This is why Hibernate is called an O/R mapping tool.

Enterprise JavaBeans are server-side components which provide another approach to persisting Java objects. In addition, enterprise beans provide declarative middleware services, such as transaction and security. Developing with enterprise beans is not as easy as developing with Hibernate, and using entity beans also requires an application server, which is difficult to set up, run, and test.

Hibernate architecture consists of three contributors: persistent objects, configuration file(s), and mapping definitions. The Hibernate API comes with three Java interfaces which are always involved in persisting the objects. These are org.hibernate.Session, org.hibernate.SessionFactory, and org.hibernate.Transaction.

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.

    Browse publications by this author
Book Title
Access this book, plus 7,500 other titles for FREE
Access now