Chapter 15. Data Access

In this chapter, you will learn how Spring can simplify your database access tasks. Data access is a common requirement for most enterprise applications, which usually require accessing data stored in relational databases. As an essential part of Java SE, Java Database Connectivity (JDBC) defines a set of standard APIs for you to access relational databases in a vendor-independent fashion.

The purpose of JDBC is to provide APIs through which you can execute SQL statements against a database. However, when using JDBC, you have to manage database-related resources by yourself and handle database exceptions explicitly. To make JDBC easier to use, Spring provides an abstraction framework for interfacing with JDBC. As the heart of the Spring JDBC framework, JDBC templates are designed to provide template methods for different types of JDBC operations. Each template method is responsible for controlling the overall process and allows you to override particular tasks of the process.

If raw JDBC doesn't satisfy your requirement or you feel your application would benefit from something slightly higher level, then Spring's support for ORM solutions will interest you. In this chapter, you will also learn how to integrate object/relational mapping (ORM) frameworks into your Spring applications. Spring supports most of the popular ORM (or data mapper) frameworks, including Hibernate, JDO, iBATIS, and the Java Persistence API (JPA). Classic TopLink isn't supported starting from Spring 3.0 (the JPA implementation's still supported, of course). However, the JPA support is varied and has support for many implementations of JPA, including the Hibernate and TopLink-based versions. The focus of this chapter will be on Hibernate and JPA. However, Spring's support for ORM frameworks is consistent, so you can easily apply the techniques in this chapter to other ORM frameworks as well.

ORM is a modern technology for persisting objects into a relational database. An ORM framework persists your objects according to the mapping metadata you provide (XML- or annotation-based), such as the mappings between classes and tables, properties and columns, and so on. It generates SQL statements for object persistence at runtime, so you needn't write database-specific SQL statements unless you want to take advantage of database-specific features or provide optimized SQL statements of your own. As a result, your application will be database independent, and it can be easily migrated to another database in the future. Compared to the direct use of JDBC, an ORM framework can significantly reduce the data access effort of your applications.

Hibernate is a popular open source and high-performance ORM framework in the Java community. Hibernate supports most JDBC-compliant databases and can use specific dialects to access particular databases. Beyond the basic ORM features, Hibernate supports more advanced features such as caching, cascading, and lazy loading. It also defines a querying language called Hibernate Query Language (HQL) for you to write simple but powerful object queries.

JPA defines a set of standard annotations and APIs for object persistence in both the Java SE and Java EE platforms. JPA is defined as part of the EJB 3.0 specification in JSR-220. JPA is just a set of standard APIs that require a JPA-compliant engine to provide persistence services. You can compare JPA with the JDBC API and a JPA engine with a JDBC driver. Hibernate can be configured as a JPA-compliant engine through an extension module called Hibernate EntityManager. This chapter will mainly demonstrate JPA with Hibernate as the underlying engine. JPA 2.0 debuted with Java EE6. Spring supports JPA 2.0 with no problems, and Spring 3.0.1 was the first release to declare a dependency on Hibernate 3.5 RC1, which in turn was the first in the Hibernate line to support JPA 2.0.

Problems with Direct JDBC

Suppose that you are going to develop an application for vehicle registration, whose major functions are the basic create, read, update, and delete (CRUD) operations on vehicle records. These records will be stored in a relational database and accessed with JDBC. First, you design the following Vehicle class, which represents a vehicle in Java:

package com.apress.springrecipes.vehicle;

public class Vehicle {

    private String vehicleNo;
    private String color;
    private int wheel;
    private int seat;

    // Constructors, Getters and Setters
    ...
}

Setting Up the Application Database

Before developing your vehicle registration application, you have to set up the database for it. For the sake of low memory consumption and easy configuration, I have chosen Apache Derby (http://db.apache.org/derby/) as my database engine. Derby is an open source relational database engine provided under the Apache License and implemented in pure Java.

Derby can run in either the embedded mode or the client/server mode. For testing purposes, the client/server mode is more appropriate because it allows you to inspect and edit data with any visual database tools that support JDBC—for example, the Eclipse Data Tools Platform (DTP).

Note

To start the Derby server in the client/server mode, just execute the startNetworkServer script for your platform (located in the bin directory of the Derby installation).

After starting up the Derby network server on localhost, you can connect to it with the JDBC properties shown in Table 15-1.

Note

You require Derby's client JDBC driver. If you are using Maven, add the following dependency to your project.

<dependency>
  <groupId>org.apache.derby</groupId>
  <artifactId>derbyclient</artifactId>
  <version>10.4.2.0</version>
 </dependency>

Table 15.1. JDBC Properties for Connecting to the Application Database

Property

Value

Driver class

org.apache.derby.jdbc.ClientDriver

URL

jdbc:derby://localhost:1527/vehicle;create=true

Username

app

Password

app

The first time you connect to this database, the database instance vehicle will be created, if it did not exist before, because you specified create=true in the URL. Note that the specification of this parameter will not cause the re-creation of the database if it already exists.

Follow these steps to connect to Derby:

  1. Open a shell on your platform.

  2. Type java –jar $DERBY_HOME/lib/derbyrun.jar ij on Unix variants or %DERBY_HOME%/lib/derbyrun.jar ij on Windows.

  3. Issue the command CONNECT 'jdbc:derby://localhost:1527/vehicle;create=true';.

You can provide any values for the username and password because Derby disables authentication by default. Next, you have to create the VEHICLE table for storing vehicle records with the following SQL statement. By default, this table will be created in the APP database sAPP database schema.

CREATE TABLE VEHICLE (
    VEHICLE_NO      VARCHAR(10)     NOT NULL,
    COLOR          VARCHAR(10),
    WHEEL          INT,
    SEAT          INT,
    PRIMARY KEY (VEHICLE_NO)
);

Understanding the Data Access Object Design Pattern

A typical design mistake made by inexperienced developers is to mix different types of logic (e.g., presentation logic, business logic, and data access logic) in a single large module. This reduces the module's reusability and maintainability because of the tight coupling it introduces. The general purpose of the Data Access Object (DAO) pattern is to avoid these problems by separating data access logic from business logic and presentation logic. This pattern recommends that data access logic be encapsulated in independent modules called data access objects.

For your vehicle registration application, you can abstract the data access operations to insert, update, delete, and query a vehicle. These operations should be declared in a DAO interface to allow for different DAO implementation technologies.

package com.apress.springrecipes.vehicle;

public interface VehicleDao {

    public void insert(Vehicle vehicle);
    public void update(Vehicle vehicle);
    public void delete(Vehicle vehicle);
    public Vehicle findByVehicleNo(String vehicleNo);
}

Most parts of the JDBC APIs declare throwing java.sql.SQLException. But because this interface aims to abstract the data access operations only, it should not depend on the implementation technology. So, it's unwise for this general interface to declare throwing the JDBC-specific SQLException. A common practice when implementing a DAO interface is to wrap this kind of exception with a runtime exception (either your own business Exception subclass or a generic one).

Implementing the DAO with JDBC

To access the database with JDBC, you create an implementation for this DAO interface (e.g., JdbcVehicleDao). Because your DAO implementation has to connect to the database to execute SQL statements, you may establish database connections by specifying the driver class name, database URL, username, and password. However, in JDBC 2.0 or higher, you can obtain database connections from a preconfigured javax.sql.DataSource object without knowing about the connection details.

package com.apress.springrecipes.vehicle;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import javax.sql.DataSource;

public class JdbcVehicleDao implements VehicleDao {

    private DataSource dataSource;
public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public void insert(Vehicle vehicle) {
        String sql = "INSERT INTO VEHICLE (VEHICLE_NO, COLOR, WHEEL, SEAT) "
                + "VALUES (?, ?, ?, ?)";
        Connection conn = null;
        try {
            conn = dataSource.getConnection();
            PreparedStatement ps = conn.prepareStatement(sql);
            ps.setString(1, vehicle.getVehicleNo());
            ps.setString(2, vehicle.getColor());
            ps.setInt(3, vehicle.getWheel());
            ps.setInt(4, vehicle.getSeat());
            ps.executeUpdate();
            ps.close();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {}
            }
        }
    }

    public Vehicle findByVehicleNo(String vehicleNo) {
        String sql = "SELECT * FROM VEHICLE WHERE VEHICLE_NO = ?";
        Connection conn = null;
        try {
            conn = dataSource.getConnection();
            PreparedStatement ps = conn.prepareStatement(sql);
            ps.setString(1, vehicleNo);

            Vehicle vehicle = null;
            ResultSet rs = ps.executeQuery();
            if (rs.next()) {
                vehicle = new Vehicle(rs.getString("VEHICLE_NO"),
                        rs.getString("COLOR"), rs.getInt("WHEEL"),
                        rs.getInt("SEAT"));
            }
            rs.close();
            ps.close();
            return vehicle;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {}
            }
        }
    }

    public void update(Vehicle vehicle) {/* ... */}

    public void delete(Vehicle vehicle) {/* ... */}
}

The vehicle insert operation is a typical JDBC update scenario. Each time this method is called, you obtain a connection from the data source and execute the SQL statement on this connection. Your DAO interface doesn't declare throwing any checked exceptions, so if a SQLException occurs, you have to wrap it with an unchecked RuntimeException. (There is a detailed discussion on handling exceptions in your DAOs later in this chapter). Don't forget to release the connection in the finally block. Failing to do so may cause your application to run out of connections.

Here, the update and delete operations will be skipped, because they are much the same as the insert operation from a technical point of view. For the query operation, you have to extract the data from the returned result set to build a vehicle object in addition to executing the SQL statement.

Configuring a Data Source in Spring

The javax.sql.DataSource interface is a standard interface defined by the JDBC specification that factories Connection instances. There are many data source implementations provided by different vendors and projects: C3PO and Apache Commons DBCP are popular open source options, and most applications servers will provide their own implementation. It is very easy to switch between different data source implementations, because they implement the common DataSource interface. As a Java application framework, Spring also provides several convenient but less powerful data source implementations. The simplest one is DriverManagerDataSource, which opens a new connection every time one is requested.

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

    <bean id="dataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName"
            value="org.apache.derby.jdbc.ClientDriver" />
        <property name="url"
            value="jdbc:derby://localhost:1527/vehicle;create=true" />
        <property name="username" value="app" />
        <property name="password" value="app" />
    </bean>
<bean id="vehicleDao"
        class="com.apress.springrecipes.vehicle.JdbcVehicleDao">
        <property name="dataSource" ref="dataSource" />
    </bean>
</beans>

DriverManagerDataSource is not an efficient data source implementation because it opens a new connection for the client every time it's requested. Another data source implementation provided by Spring is SingleConnectionDataSource (a DriverManagerDataSource subclass). As its name indicates, this maintains only a single connection that's reused all the time and never closed. Obviously, it is not suitable in a multithreaded environment.

Spring's own data source implementations are mainly used for testing purposes. However, many production data source implementations support connection pooling. For example, the Database Connection Pooling Services (DBCP) module of the Apache Commons Library has several data source implementations that support connection pooling. Of these, BasicDataSource accepts the same connection properties as DriverManagerDataSource and allows you to specify the initial connection size and maximum active connections for the connection pool.

<bean id="dataSource"
    class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName"
        value="org.apache.derby.jdbc.ClientDriver" />
    <property name="url"
        value="jdbc:derby://localhost:1527/vehicle;create=true" />
    <property name="username" value="app" />
    <property name="password" value="app" />
    <property name="initialSize" value="2" />
    <property name="maxActive" value="5" />
</bean>

bean configuration file.

Table 15.2. TestContext Support Classes for Transaction Management

Testing Framework

TestContext Support Class[a]

JUnit 3

AbstractTransactionalJUnit38SpringContextTests

JUnit 4

AbstractTransactionalJUnit4SpringContextTests

TestNG

AbstractTransactionalTestNGSpringContextTests

[a] These three TestContext support classes have TransactionalTestExecutionListener enabled in addition to DependencyInjectionTestExecutionListener and DirtiesContextTestExecutionListener.

In JUnit 4 and TestNG, you can simply annotate @Transactional at the class level or the method level to have the test methods run within transactions, without extending a TestContext support class. However, to integrate with a test context manager, you have to run the JUnit 4 test with the test runner SpringJUnit4ClassRunner, and you have to do it manually for a TestNG test.

How It Works

Let's consider storing your bank system's accounts in a relational database. You can choose any JDBC-compliant database engine that supports transactions and then execute the following SQL statement on it to create the ACCOUNT table. Here, we have chosen Apache Derby as our database engine and created the table in the bank instance.

CREATE TABLE ACCOUNT (
    ACCOUNT_NO      VARCHAR(10)     NOT NULL,
    BALANCE          DOUBLE         NOT NULL,
    PRIMARY KEY (ACCOUNT_NO)
);

Next, you create a new DAO implementation that uses JDBC to access the database. You can take advantage of SimpleJdbcTemplate to simplify your operations.

package com.apress.springrecipes.bank;

import org.springframework.jdbc.core.simple.SimpleJdbcDaoSupport;

public class JdbcAccountDao extends SimpleJdbcDaoSupport implements AccountDao {

    public void createAccount(Account account) {
        String sql = "INSERT INTO ACCOUNT (ACCOUNT_NO, BALANCE) VALUES (?, ?)";
        getSimpleJdbcTemplate().update(
                sql, account.getAccountNo(), account.getBalance());
    }

    public void updateAccount(Account account) {
        String sql = "UPDATE ACCOUNT SET BALANCE = ? WHERE ACCOUNT_NO = ?";
        getSimpleJdbcTemplate().update(
                sql, account.getBalance(), account.getAccountNo());
    }

    public void removeAccount(Account account) {
        String sql = "DELETE FROM ACCOUNT WHERE ACCOUNT_NO = ?";
        getSimpleJdbcTemplate().update(sql, account.getAccountNo());
    }

    public Account findAccount(String accountNo) {
        String sql = "SELECT BALANCE FROM ACCOUNT WHERE ACCOUNT_NO = ?";
        double balance = getSimpleJdbcTemplate().queryForObject(
                sql, Double.class, accountNo);
        return new Account(accountNo, balance);
    }
}

Before you create integration tests to test the AccountService instance that uses this DAO to persist account objects, you have to replace InMemoryAccountDao with this DAO in the bean configuration file, and configure the target data source as well.

Note

To use the data source implementations provided by DBCP, you have to add them to your CLASSPATH. If you are using Maven, add the following dependency to your project:

<dependency>
  <groupId>commons-dbcp</groupId>
  <artifactId>commons-dbcp</artifactId>
   <version>1.2.1</version>
 </dependency>

Many Java EE application servers build in data source implementations that you can configure from the server console or in configuration files. If you have a data source configured in an application server and exposed for JNDI lookup, you can use JndiObjectFactoryBean to look it up.

<bean id="dataSource"
    class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName" value="jdbc/VehicleDS" />
</bean>

In Spring, a JNDI lookup can be simplified by the jndi-lookup element defined in the jee sjee schema.

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:jee="http://www.springframework.org/schema/jee"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/jee
        http://www.springframework.org/schema/jee/spring-jee-3.0.xsd">

    <jee:jndi-lookup id="dataSource" jndi-name="jdbc/VehicleDS" />
    ...
</beans>

Running the DAO

The following Main class tests your DAO by using it to insert a new vehicle to the database. If it succeeds, you can query the vehicle from the database immediately.

package com.apress.springrecipes.vehicle;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {

    public static void main(String[] args) {
        ApplicationContext context =
            new ClassPathXmlApplicationContext("beans.xml");

        VehicleDao vehicleDao = (VehicleDao) context.getBean("vehicleDao");
        Vehicle vehicle = new Vehicle("TEM0001", "Red", 4, 4);
        vehicleDao.insert(vehicle);

        vehicle = vehicleDao.findByVehicleNo("TEM0001");
        System.out.println("Vehicle No: " + vehicle.getVehicleNo());
        System.out.println("Color: " + vehicle.getColor());
        System.out.println("Wheel: " + vehicle.getWheel());
        System.out.println("Seat: " + vehicle.getSeat());
    }
}

Now you can implement a DAO using JDBC directly. However, as you can see from the preceding DAO implementation, most of the JDBC code is similar and needs to be repeated for each database operation. Such redundant code will make your DAO methods much longer and less readable.

Taking It A Step Further

An alternative approach is to use an ORM (an object/relational mapping) tool, which lets you code the logic specifically for mapping an entity in your domain model to a database table. The ORM will, in turn, figure out how to write the logic to usefully persist your class's data to the database. This can be very liberating: you are suddenly beholden only to your business and domain model, not to whims of your database' SQL parser. The flip side, of course, is that you are also divesting yourself from the complete control over the communication between your client and the database—you have to trust that the ORM layer will do the right thing.

Using a JDBC Template to Update a Database

Problem

Using JDBC is tedious and fraught with redundant API calls, many of which could be managed for you. To implement a JDBC update operation, you have to perform the following tasks, most of which are redundant:

  1. Obtain a database connection from the data source.

  2. Create a PreparedStatement object from the connection.

  3. Bind the parameters to the PreparedStatement object.

  4. Execute the PreparedStatement object.

  5. Handle SQLException.

  6. Clean up the statement object and connection.

JDBC is a very low-level API, but with the JDBC template, the surface area of the API that you need to work with becomes more expressive (you spend less time in the weeds and more time working on your application logic) and is simpler to work with safely.

Solution

The org.springframework.jdbc.core.JdbcTemplate class declares a number of overloaded update() template methods to control the overall update process. Different versions of the update() method allow you to override different task subsets of the default process. The Spring JDBC framework predefines several callback interfaces to encapsulate different task subsets. You can implement one of these callback interfaces and pass its instance to the corresponding update() method to complete the process.

How It Works

Updating a Database with a Statement Creator

The first callback interface to introduce is PreparedStatementCreator. You implement this interface to override the statement creation task (task 2) and the parameter binding task (task 3) of the overall update process. To insert a vehicle into the database, you implement the PreparedStatementCreator interface as follows:

package com.apress.springrecipes.vehicle;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import org.springframework.jdbc.core.PreparedStatementCreator;

public class InsertVehicleStatementCreator implements PreparedStatementCreator {

    private Vehicle vehicle;

    public InsertVehicleStatementCreator(Vehicle vehicle) {
        this.vehicle = vehicle;
    }

    public PreparedStatement createPreparedStatement(Connection conn)
            throws SQLException {
        String sql = "INSERT INTO VEHICLE (VEHICLE_NO, COLOR, WHEEL, SEAT) "
                + "VALUES (?, ?, ?, ?)";
        PreparedStatement ps = conn.prepareStatement(sql);
        ps.setString(1, vehicle.getVehicleNo());
        ps.setString(2, vehicle.getColor());
ps.setInt(3, vehicle.getWheel());
        ps.setInt(4, vehicle.getSeat());
        return ps;
    }
}

When implementing the PreparedStatementCreator interface, you will get the database connection as the createPreparedStatement() method's argument. All you have to do in this method is to create a PreparedStatement object on this connection and bind your parameters to this object. Finally, you have to return the PreparedStatement object as the method's return value. Notice that the method signature declares throwing SQLException, which means that you don't need to handle this kind of exception yourself.

Now, you can use this statement creator to simplify the vehicle insert operation. First of all, you have to create an instance of the JdbcTemplate class and pass in the data source for this template to obtain a connection from it. Then, you just make a call to the update() method and pass in your statement creator for the template to complete the update process.

package com.apress.springrecipes.vehicle;
...
import org.springframework.jdbc.core.JdbcTemplate;

public class JdbcVehicleDao implements VehicleDao {
    ...
    public void insert(Vehicle vehicle) {
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
        jdbcTemplate.update(new InsertVehicleStatementCreator(vehicle));
    }
}

Typically, it is better to implement the PreparedStatementCreator interface and other callback interfaces as inner classes if they are used within one method only. This is because you can get access to the local variables and method arguments directly from the inner class, instead of passing them as constructor arguments. The only constraint on such variables and arguments is that they must be declared as final.

package com.apress.springrecipes.vehicle;
...
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;

public class JdbcVehicleDao implements VehicleDao {
    ...
    public void insert(final Vehicle vehicle) {
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);

        jdbcTemplate.update(new PreparedStatementCreator() {
public PreparedStatement createPreparedStatement(Connection conn)
                    throws SQLException {
                String sql = "INSERT INTO VEHICLE "
                        + "(VEHICLE_NO, COLOR, WHEEL, SEAT) "
                        + "VALUES (?, ?, ?, ?)";
                PreparedStatement ps = conn.prepareStatement(sql);
                ps.setString(1, vehicle.getVehicleNo());
                ps.setString(2, vehicle.getColor());
                ps.setInt(3, vehicle.getWheel());
                ps.setInt(4, vehicle.getSeat());
                return ps;
            }
        });
    }
}

Now, you can delete the preceding InsertVehicleStatementCreator class, because it will not be used anymore.

Updating a Database with a Statement Setter

The second callback interface, PreparedStatementSetter, as its name indicates, performs only the parameter binding task (task 3) of the overall update process.

package com.apress.springrecipes.vehicle;
...
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementSetter;

public class JdbcVehicleDao implements VehicleDao {
    ...
    public void insert(final Vehicle vehicle) {
        String sql = "INSERT INTO VEHICLE (VEHICLE_NO, COLOR, WHEEL, SEAT) "
                + "VALUES (?, ?, ?, ?)";
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);

        jdbcTemplate.update(sql, new PreparedStatementSetter() {

                    public void setValues(PreparedStatement ps)
                            throws SQLException {
                        ps.setString(1, vehicle.getVehicleNo());
                        ps.setString(2, vehicle.getColor());
                        ps.setInt(3, vehicle.getWheel());
                        ps.setInt(4, vehicle.getSeat());
                    }
                });
    }
}

Another version of the update() template method accepts a SQL statement and a PreparedStatementSetter object as arguments. This method will create a PreparedStatement object for you from your SQL statement. All you have to do with this interface is to bind your parameters to the PreparedStatement object.

Updating a Database with a SQL Statement and Parameter Values

Finally, the simplest version of the update() method accepts a SQL statement and an object array as statement parameters. It will create a PreparedStatement object from your SQL statement and bind the parameters for you. Therefore, you don't have to override any of the tasks in the update process.

package com.apress.springrecipes.vehicle;
...
import org.springframework.jdbc.core.JdbcTemplate;

public class JdbcVehicleDao implements VehicleDao {
    ...
    public void insert(final Vehicle vehicle) {
        String sql = "INSERT INTO VEHICLE (VEHICLE_NO, COLOR, WHEEL, SEAT) "
                + "VALUES (?, ?, ?, ?)";
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);

        jdbcTemplate.update(sql, new Object[] { vehicle.getVehicleNo(),
                vehicle.getColor(),vehicle.getWheel(), vehicle.getSeat() });
    }
}

Of the three different versions of the update() method introduced, the last is the simplest because you don't have to implement any callback interfaces. Additionally, we've managed to remove all setX (setInt, setString, etc.)–style methods for parameterizing the query. In contrast, the first is the most flexible because you can do any preprocessing of the PreparedStatement object before its execution. In practice, you should always choose the simplest version that meets all your needs.

There are also other overloaded update() methods provided by the JdbcTemplate class. Please refer to Javadoc for details.

Batch Updating a Database

Suppose that you want to insert a batch of vehicles into the database. If you call the insert() method multiple times, the update will be very slow as the SQL statement will be compiled repeatedly. So, it would be better to add a new method to the DAO interface for inserting a batch of vehicles.

package com.apress.springrecipes.vehicle;
...
public interface VehicleDao {
    ...
    public void insertBatch(List<Vehicle> vehicles);
}

The JdbcTemplate class also offers the batchUpdate() template method for batch update operations. It requires a SQL statement and a BatchPreparedStatementSetter object as arguments. In this method, the statement is compiled (prepared) only once and executed multiple times. If your database driver supports JDBC 2.0, this method automatically makes use of the batch update features to increase performance.

package com.apress.springrecipes.vehicle;
...
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcTemplate;

public class JdbcVehicleDao implements VehicleDao {
    ...
    public void insertBatch(final List<Vehicle> vehicles) {
        String sql = "INSERT INTO VEHICLE (VEHICLE_NO, COLOR, WHEEL, SEAT) "
                + "VALUES (?, ?, ?, ?)";
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);

        jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {

                    public int getBatchSize() {
                        return vehicles.size();
                    }

                    public void setValues(PreparedStatement ps, int i)
                            throws SQLException {
                        Vehicle vehicle = vehicles.get(i);
                        ps.setString(1, vehicle.getVehicleNo());
                        ps.setString(2, vehicle.getColor());
                        ps.setInt(3, vehicle.getWheel());
                        ps.setInt(4, vehicle.getSeat());
                    }
                });
    }
}

You can test your batch insert operation with the following code snippet in the Main cMain class:

package com.apress.springrecipes.vehicle;
...
public class Main {

    public static void main(String[] args) {
        ...
        VehicleDao vehicleDao = (VehicleDao) context.getBean("vehicleDao");
        Vehicle vehicle1 = new Vehicle("TEM0002", "Blue", 4, 4);
        Vehicle vehicle2 = new Vehicle("TEM0003", "Black", 4, 6);
        vehicleDao.insertBatch(
                Arrays.asList(new Vehicle[] { vehicle1, vehicle2 }));
    }
}

Using a JDBC Template to Query a Database

Problem

To implement a JDBC query operation, you have to perform the following tasks, two of which (tasks 5 and 6) are additional as compared to an update operation:

  1. Obtain a database connection from the data source.

  2. Create a PreparedStatement object from the connection.

  3. Bind the parameters to the PreparedStatement object.

  4. Execute the PreparedStatement object.

  5. Iterate the returned result set.

  6. Extract data from the result set.

  7. Handle SQLException.

  8. Clean up the statement object and connection.

The only steps relevant to your business logic, however, are the definition of the query and the extraction of the results from the result set! The rest is better handled by the JDBC template.

Solution

The JdbcTemplate class declares a number of overloaded query() template methods to control the overall query process. You can override the statement creation (task 2) and the parameter binding (task 3) by implementing the PreparedStatementCreator and PreparedStatementSetter interfaces, just as you did for the update operations. Moreover, the Spring JDBC framework supports multiple ways for you to override the data extraction (task 6).

How It Works

Extracting Data with Row Callback Handler

RowCallbackHandler is the is the primary interface that allows you to process the current row of the result set. One of the query() methods iterates the result set for you and calls your RowCallbackHandler for each row. So, the processRow() method will be called once for each row of the returned result set.

package com.apress.springrecipes.vehicle;
...
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowCallbackHandler;
public class JdbcVehicleDao implements VehicleDao {
    ...
    public Vehicle findByVehicleNo(String vehicleNo) {
        String sql = "SELECT * FROM VEHICLE WHERE VEHICLE_NO = ?";
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);

        final Vehicle vehicle = new Vehicle();
        jdbcTemplate.query(sql, new Object[] { vehicleNo },
                new RowCallbackHandler() {
                    public void processRow(ResultSet rs) throws SQLException {
                        vehicle.setVehicleNo(rs.getString("VEHICLE_NO"));
                        vehicle.setColor(rs.getString("COLOR"));
                        vehicle.setWheel(rs.getInt("WHEEL"));
                        vehicle.setSeat(rs.getInt("SEAT"));
                    }
                });
        return vehicle;
    }
}

As there will be one row returned for the SQL query at maximum, you can create a vehicle object as a local variable and set its properties by extracting data from the result set. For a result set with more than one row, you should collect the objects as a list.

Extracting Data with a Row Mapper

The RowMapper<T> interface is more general than RowCallbackHandler. Its purpose is to map a single row of the result set to a customized object, so it can be applied to a single-row result set as well as a multiple-row result set. From the viewpoint of reuse, it's better to implement the RowMapper<T> interface as a normal class than as an inner class. In the mapRow() method of this interface, you have to construct the object that represents a row and return it as the method's return value.

package com.apress.springrecipes.vehicle;

import java.sql.ResultSet;
import java.sql.SQLException;

import org.springframework.jdbc.core.RowMapper;

public class VehicleRowMapper implements RowMapper<Vehicle> {

    public Vehicle mapRow(ResultSet rs, int rowNum) throws SQLException {
        Vehicle vehicle = new Vehicle();
        vehicle.setVehicleNo(rs.getString("VEHICLE_NO"));
        vehicle.setColor(rs.getString("COLOR"));
        vehicle.setWheel(rs.getInt("WHEEL"));
        vehicle.setSeat(rs.getInt("SEAT"));
        return vehicle;
    }
}

As mentioned, RowMapper<T> can be used for either a single-row or multiple-row result set. When querying for a unique object like in findByVehicleNo(), you have to make a call to the queryForObject() method of JdbcTemplate.

package com.apress.springrecipes.vehicle;
...
import org.springframework.jdbc.core.JdbcTemplate;

public class JdbcVehicleDao implements VehicleDao {
    ...
    public Vehicle findByVehicleNo(String vehicleNo) {
        String sql = "SELECT * FROM VEHICLE WHERE VEHICLE_NO = ?";
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);

        Vehicle vehicle = (Vehicle) jdbcTemplate.queryForObject(sql,
                new Object[] { vehicleNo }, new VehicleRowMapper());
        return vehicle;
    }
}

Spring comes with a convenient RowMapper<T> implementation, BeanPropertyRowMapper<T>, which can automatically map a row to a new instance of the specified class. Note that the specified class must be a top-level class and must have a default or no-argument constructor. It first instantiates this class and then maps each column value to a property by matching their names. It supports matching a property name (e.g., vehicleNo) to the same column name or the column name with underscores (e.g., VEHICLE_NO).

package com.apress.springrecipes.vehicle;
...
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;

public class JdbcVehicleDao implements VehicleDao {

   ...

   public Vehicle findByVehicleNo(String vehicleNo) {
       String sql = "SELECT * FROM VEHICLE WHERE VEHICLE_NO = ?";
       BeanPropertyRowMapper<Vehicle> vehicleRowMapper =
                BeanPropertyRowMapper.newInstance(Vehicle.class);
       Vehicle vehicle = getSimpleJdbcTemplate().queryForObject(
                sql, vehicleRowMapper, vehicleNo);
        return vehicle;
    }
}

Querying for Multiple Rows

Now, let's look at how to query for a result set with multiple rows. For example, suppose that you need a findAll() method in the DAO interface to get all vehicles.

package com.apress.springrecipes.vehicle;
...
public interface VehicleDao {
    ...
    public List<Vehicle> findAll();
}

Without the help of RowMapper<T>, you can still call the queryForList() method and pass in a SQL statement. The returned result will be a list of maps. Each map stores a row of the result set with the column names as the keys.

package com.apress.springrecipes.vehicle;
...
import org.springframework.jdbc.core.JdbcTemplate;

public class JdbcVehicleDao implements VehicleDao {
    ...
    public List<Vehicle> findAll() {
        String sql = "SELECT * FROM VEHICLE";
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);

        List<Vehicle> vehicles = new ArrayList<Vehicle>();
        List<Map<String,Object>> rows = jdbcTemplate.queryForList(sql);
        for (Map<String, Object> row : rows) {
            Vehicle vehicle = new Vehicle();
            vehicle.setVehicleNo((String) row.get("VEHICLE_NO"));
            vehicle.setColor((String) row.get("COLOR"));
            vehicle.setWheel((Integer) row.get("WHEEL"));
            vehicle.setSeat((Integer) row.get("SEAT"));
            vehicles.add(vehicle);
        }
        return vehicles;
    }
}

You can test your findAll() method with the following code snippet in the Main class:

package com.apress.springrecipes.vehicle;
...
public class Main {

    public static void main(String[] args) {
        ...
        VehicleDao vehicleDao = (VehicleDao) context.getBean("vehicleDao");
        List<Vehicle> vehicles = vehicleDao.findAll();
        for (Vehicle vehicle : vehicles) {
            System.out.println("Vehicle No: " + vehicle.getVehicleNo());
            System.out.println("Color: " + vehicle.getColor());
System.out.println("Wheel: " + vehicle.getWheel());
            System.out.println("Seat: " + vehicle.getSeat());
        }
    }
}

If you use a RowMapper<T> object to map the rows in a result set, you will get a list of mapped objects from the query() mquery() method.

package com.apress.springrecipes.vehicle;
...
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;

public class JdbcVehicleDao implements VehicleDao {
   ...
  public List<Vehicle> findAll() {
     String sql = "SELECT * FROM VEHICLE";
     RowMapper<Vehicle> rm =
BeanPropertyRowMapper.newInstance(Vehicle.class);
     List<Vehicle> vehicles = getSimpleJdbcTemplate().query(sql, rm);
     return vehicles;
   }
}

Querying for a Single Value

Finally, let's consider to query for a single-row and single-column result set. As an example, add the following operations to the DAO interface:

package com.apress.springrecipes.vehicle;
...
public interface VehicleDao {
    ...
    public String getColor(String vehicleNo);
    public int countAll();
}

To query for a single string value, you can call the overloaded queryForObject() method, which requires an argument of java.lang.Class type. This method will help you to map the result value to the type you specified. For integer values, you can call the convenient method queryForInt().

package com.apress.springrecipes.vehicle;
...
import org.springframework.jdbc.core.JdbcTemplate;

public class JdbcVehicleDao implements VehicleDao {
    ...
public String getColor(String vehicleNo) {
        String sql = "SELECT COLOR FROM VEHICLE WHERE VEHICLE_NO = ?";
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);

        String color = (String) jdbcTemplate.queryForObject(sql,
                new Object[] { vehicleNo }, String.class);
        return color;
    }

    public int countAll() {
        String sql = "SELECT COUNT(*) FROM VEHICLE";
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);

        int count = jdbcTemplate.queryForInt(sql);
        return count;
    }
}

You can test these two methods with the following code snippet in the Main cMain class:

package com.apress.springrecipes.vehicle;
...
public class Main {

    public static void main(String[] args) {
        ...
        VehicleDao vehicleDao = (VehicleDao) context.getBean("vehicleDao");
        int count = vehicleDao.countAll();
        System.out.println("Vehicle Count: " + count);
        String color = vehicleDao.getColor("TEM0001");
        System.out.println("Color for [TEM0001]: " + color);
    }
}

Simplifying JDBC Template Creation

Problem

It's not efficient to create a new instance of JdbcTemplate every time you use it, because you have to repeat the creation statement and incur the cost of creating a new object.

Solution

The JdbcTemplate class is designed to be thread-safe, so you can declare a single instance of it in the IoC container and inject this instance into all your DAO instances. Furthermore, the Spring JDBC framework offers a convenient class, org.springframework.jdbc.core.support.JdbcDaoSupport, to simplify your DAO implementation. This class declares a jdbcTemplate property, which can be injected from the IoC container or created automatically from a data source, for example, JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource). Your DAO can extend this class to have this property inherited.

How It Works

Injecting a JDBC Template

Until now, you have created a new instance of JdbcTemplate in each DAO method. Actually, you can have it injected at the class level and use this injected instance in all DAO methods. For simplicity's sake, the following code shows only the change to the insert() method:

package com.apress.springrecipes.vehicle;
...
import org.springframework.jdbc.core.JdbcTemplate;

public class JdbcVehicleDao implements VehicleDao {

    private JdbcTemplate jdbcTemplate;

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public void insert(final Vehicle vehicle) {
        String sql = "INSERT INTO VEHICLE (VEHICLE_NO, COLOR, WHEEL, SEAT) "
                + "VALUES (?, ?, ?, ?)";

        jdbcTemplate.update(sql, new Object[] { vehicle.getVehicleNo(),
                vehicle.getColor(), vehicle.getWheel(), vehicle.getSeat() });

    }
    ...
}

A JDBC template requires a data source to be set. You can inject this property by either a setter method or a constructor argument. Then, you can inject this JDBC template into your DAO.

<beans ...>
    ...
    <bean id="jdbcTemplate"
        class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <bean id="vehicleDao"
        class="com.apress.springrecipes.vehicle.JdbcVehicleDao">
        <property name="jdbcTemplate" ref="jdbcTemplate" />
    </bean>
</beans>

Extending the JdbcDaoSupport Class

The org.springframework.jdbc.core.support.JdbcDaoSupport class has a setDataSource() method and a setJdbcTemplate() method. Your DAO class can extend this class to have these methods inherited. Then, you can either inject a JDBC template directly or inject a data source for it to create a JDBC template. The following code fragment is taken from Spring's JdbcDaoSupport class:

package org.springframework.jdbc.core.support;
...
public abstract class JdbcDaoSupport extends DaoSupport {

    private JdbcTemplate jdbcTemplate;

    public final void setDataSource(DataSource dataSource) {
       if( this.jdbcTemplate == null || dataSource != this.jdbcTemplate.
Extending the JdbcDaoSupport Class
getDataSource() ){ this.jdbcTemplate = createJdbcTemplate(dataSource); initTemplateConfig(); } } ... public final void setJdbcTemplate(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; initTemplateConfig(); } public final JdbcTemplate getJdbcTemplate() { return this.jdbcTemplate; } ... }

In your DAO methods, you can simply call the getJdbcTemplate() method to retrieve the JDBC template. You also have to delete the dataSource and jdbcTemplate properties, as well as their setter methods, from your DAO class, because they have already been inherited. Again, for simplicity's sake, only the change to the insert() method is shown.

package com.apress.springrecipes.vehicle;
...
import org.springframework.jdbc.core.support.JdbcDaoSupport;

public class JdbcVehicleDao extends JdbcDaoSupport implements VehicleDao {

    public void insert(final Vehicle vehicle) {
        String sql = "INSERT INTO VEHICLE (VEHICLE_NO, COLOR, WHEEL, SEAT) "
                + "VALUES (?, ?, ?, ?)";
getJdbcTemplate().update(sql, new Object[] { vehicle.getVehicleNo(),
                vehicle.getColor(), vehicle.getWheel(), vehicle.getSeat() });
    }
    ...
}

By extending JdbcDaoSupport, your DAO class inherits the setDataSource() method. You can inject a data source into your DAO instance for it to create a JDBC template.

<beans ...>
    ...
    <bean id="vehicleDao"
        class="com.apress.springrecipes.vehicle.JdbcVehicleDao">
        <property name="dataSource" ref="dataSource" />
    </bean>
</beans>

Using the Simple JDBC Template with Java 1.5

Problem

The JdbcTemplate class works fine in most circumstances, but it can be further improved to take advantage of the Java 1.5 features.

Solution

org.springframework.jdbc.core.simple.SimpleJdbcTemplate is an evolution of JdbcTemplate that takes advantage of Java 1.5 features such as auto-boxing, generics, and variable-length arguments to simplify its usage.

How It Works

Using a Simple JDBC Template to Update a Database

Many of the methods in the classic JdbcTemplate require statement parameters to be passed as an object array. In SimpleJdbcTemplate, they can be passed as variable-length arguments; this saves you the trouble of wrapping them in an array. To use SimpleJdbcTemplate, you can either instantiate it directly or retrieve its instance by extending the SimpleJdbcDaoSupport class.

package com.apress.springrecipes.vehicle;
...
import org.springframework.jdbc.core.simple.SimpleJdbcDaoSupport;
public class JdbcVehicleDao extends SimpleJdbcDaoSupport implements
        VehicleDao {

    public void insert(Vehicle vehicle) {
        String sql = "INSERT INTO VEHICLE (VEHICLE_NO, COLOR, WHEEL, SEAT) "
                + "VALUES (?, ?, ?, ?)";

        getSimpleJdbcTemplate().update(sql, vehicle.getVehicleNo(),
                vehicle.getColor(), vehicle.getWheel(), vehicle.getSeat());
    }
    ...
}

SimpleJdbcTemplate offers a convenient batch update method for you to specify a SQL statement and a batch of parameters in the form of List<Object[]> so that you don't need to implement the BatchPreparedStatementSetter interface. Note that SimpleJdbcTemplate requires either a DataSource or a JdbcTemplate.

package com.apress.springrecipes.vehicle;
...
import org.springframework.jdbc.core.simple.SimpleJdbcDaoSupport;

public class JdbcVehicleDao extends SimpleJdbcDaoSupport implements VehicleDao {
    ...
    public void insertBatch(List<Vehicle> vehicles) {
        String sql = "INSERT INTO VEHICLE (VEHICLE_NO, COLOR, WHEEL, SEAT) "
                + "VALUES (?, ?, ?, ?)";

        List<Object[]> parameters = new ArrayList<Object[]>();
        for (Vehicle vehicle : vehicles) {
            parameters.add(new Object[] { vehicle.getVehicleNo(),
                    vehicle.getColor(), vehicle.getWheel(), vehicle.getSeat() });
        }
        getSimpleJdbcTemplate().batchUpdate(sql, parameters);
    }
}

Using a Simple JDBC Template to Query a Database

When implementing the RowMapper<T> interface, the return type of the mapRow() method is java.lang.Object. ParameterizedRowMapper<T> is a subinterface that takes a type parameter as the return type of the mapRow() method.

package com.apress.springrecipes.vehicle;
...
import org.springframework.jdbc.core.simple.ParameterizedRowMapper;

public class VehicleRowMapper implements ParameterizedRowMapper<Vehicle> {
public Vehicle mapRow(ResultSet rs, int rowNum) throws SQLException {
        Vehicle vehicle = new Vehicle();
        vehicle.setVehicleNo(rs.getString("VEHICLE_NO"));
        vehicle.setColor(rs.getString("COLOR"));
        vehicle.setWheel(rs.getInt("WHEEL"));
        vehicle.setSeat(rs.getInt("SEAT"));
        return vehicle;
    }
}

Using SimpleJdbcTemplate with ParameterizedRowMapper<T> can save you the trouble of casting the type of the returned result. For the queryForObject() method, the return type is determined by the ParameterizedRowMapper<T> object's type parameter, which is Vehicle in this case. Note that the statement parameters must be supplied at the end of the argument list since they are of variable length.

package com.apress.springrecipes.vehicle;
...
import org.springframework.jdbc.core.simple.SimpleJdbcDaoSupport;

public class JdbcVehicleDao extends SimpleJdbcDaoSupport implements
        VehicleDao {
    ...
    public Vehicle findByVehicleNo(String vehicleNo) {
        String sql = "SELECT * FROM VEHICLE WHERE VEHICLE_NO = ?";

        // No need to cast into Vehicle anymore.
        Vehicle vehicle = getSimpleJdbcTemplate().queryForObject(sql,
                new VehicleRowMapper(), vehicleNo);
        return vehicle;
    }
}

Spring also comes with a convenient ParameterizedRowMapper<T> implementation, ParameterizedBeanPropertyRowMapper<T>, which can automatically map a row to a new instance of the specified class.

package com.apress.springrecipes.vehicle;
...
import org.springframework.jdbc.core.simple.ParameterizedBeanPropertyRowMapper;
import org.springframework.jdbc.core.simple.SimpleJdbcDaoSupport;

public class JdbcVehicleDao extends SimpleJdbcDaoSupport implements
        VehicleDao {
    ...
    public Vehicle findByVehicleNo(String vehicleNo) {
        String sql = "SELECT * FROM VEHICLE WHERE VEHICLE_NO = ?";
Vehicle vehicle = getSimpleJdbcTemplate().queryForObject(sql,
                ParameterizedBeanPropertyRowMapper.newInstance(Vehicle.class),
                vehicleNo);
        return vehicle;
    }
}

When using the classic JdbcTemplate, the findAll() method has a warning from the Java compiler because of an unchecked conversion from List to List<Vehicle>. This is because the return type of the query() method is List rather than the type-safe List<Vehicle>. After switching to SimpleJdbcTemplate and ParameterizedBeanPropertyRowMapper<T>, the warning will be eliminated immediately because the returned List is parameterized with the same type as the ParameterizedRowMapper<T> argument.

package com.apress.springrecipes.vehicle;
...
import org.springframework.jdbc.core.simple.ParameterizedBeanPropertyRowMapper;
import org.springframework.jdbc.core.simple.SimpleJdbcDaoSupport;

public class JdbcVehicleDao extends SimpleJdbcDaoSupport implements
        VehicleDao {
    ...
    public List<Vehicle> findAll() {
        String sql = "SELECT * FROM VEHICLE";

        List<Vehicle> vehicles = getSimpleJdbcTemplate().query(sql,
                ParameterizedBeanPropertyRowMapper.newInstance(Vehicle.class));
        return vehicles;
    }
}

When querying for a single value with SimpleJdbcTemplate, the return type of the queryForObject() method will be determined by the class argument (e.g., String.class). So, there's no need for you to perform type casting manually. Note that the statement parameters of variable length must also be supplied at the end of the argument list.

package com.apress.springrecipes.vehicle;
...
import org.springframework.jdbc.core.simple.SimpleJdbcDaoSupport;

public class JdbcVehicleDao extends SimpleJdbcDaoSupport implements
        VehicleDao {
    ...
    public String getColor(String vehicleNo) {
        String sql = "SELECT COLOR FROM VEHICLE WHERE VEHICLE_NO = ?";

        // No need to cast into String anymore.
        String color = getSimpleJdbcTemplate().queryForObject(sql,
                String.class, vehicleNo);
        return color;
    }
}

Using Named Parameters in a JDBC Template

Problem

In classic JDBC usage, SQL parameters are represented by the placeholder ? and are bound by position. The trouble with positional parameters is that whenever the parameter order is changed, you have to change the parameter bindings as well. For a SQL statement with many parameters, it is very cumbersome to match the parameters by position.

Solution

Another option when binding SQL parameters in the Spring JDBC framework is to use named parameters. As the term implies, named SQL parameters are specified by name (starting with a colon) rather than by position. Named parameters are easier to maintain and also improve readability. At runtime, the framework classes replace named parameters with placeholders. Named parameters are supported only in SimpleJdbcTemplate and NamedParameterJdbcTemplate.

How It Works

When using named parameters in your SQL statement, you can provide the parameter values in a map with the parameter names as the keys.

package com.apress.springrecipes.vehicle;
...
import org.springframework.jdbc.core.simple.SimpleJdbcDaoSupport;

public class JdbcVehicleDao extends SimpleJdbcDaoSupport implements
        VehicleDao {

    public void insert(Vehicle vehicle) {
        String sql = "INSERT INTO VEHICLE (VEHICLE_NO, COLOR, WHEEL, SEAT) "
                + "VALUES (:vehicleNo, :color, :wheel, :seat)";

        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("vehicleNo", vehicle.getVehicleNo());
        parameters.put("color", vehicle.getColor());
        parameters.put("wheel", vehicle.getWheel());
        parameters.put("seat", vehicle.getSeat());

        getSimpleJdbcTemplate().update(sql, parameters);
    }
    ...
}

You can also provide a SQL parameter source, whose responsibility is to offer SQL parameter values for named SQL parameters. There are three implementations of the SqlParameterSource interface. The basic one is MapSqlParameterSource, which wraps a map as its parameter source. In this example, this is a net-loss compared to the previous example, as we've introduced one extra object—the SqlParameterSource:

package com.apress.springrecipes.vehicle;
...
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.core.simple.SimpleJdbcDaoSupport;

public class JdbcVehicleDao extends SimpleJdbcDaoSupport implements
        VehicleDao {

    public void insert(Vehicle vehicle) {
        String sql = "INSERT INTO VEHICLE (VEHICLE_NO, COLOR, WHEEL, SEAT) "
                + "VALUES (:vehicleNo, :color, :wheel, :seat)";

        Map<String, Object> parameters = new HashMap<String, Object>();
        ...
        SqlParameterSource parameterSource =
            new MapSqlParameterSource(parameters);

        getSimpleJdbcTemplate().update(sql, parameterSource);
    }
    ...
}

The power comes when we need an extra level of indirection between the parameters passed into the update method and the source of their values. For example, what if we want to get properties from a JavaBean? Here is where the SqlParameterSource intermediary starts to benefit us! SqlParameterSource is BeanPropertySqlParameterSource, which wraps a normal Java object as a SQL parameter source. For each of the named parameters, the property with the same name will be used as the parameter value.

package com.apress.springrecipes.vehicle;
...
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.core.simple.SimpleJdbcDaoSupport;

public class JdbcVehicleDao extends SimpleJdbcDaoSupport implements
        VehicleDao {

    public void insert(Vehicle vehicle) {
        String sql = "INSERT INTO VEHICLE (VEHICLE_NO, COLOR, WHEEL, SEAT) "
                + "VALUES (:vehicleNo, :color, :wheel, :seat)";
SqlParameterSource parameterSource =
            new BeanPropertySqlParameterSource(vehicle);

        getSimpleJdbcTemplate().update(sql, parameterSource);
    }
    ...
}

Named parameters can also be used in batch update. You can provide either a Map array or a SqlParameterSource array for the parameter values.

package com.apress.springrecipes.vehicle;
...
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.core.simple.SimpleJdbcDaoSupport;

public class JdbcVehicleDao extends SimpleJdbcDaoSupport implements VehicleDao {
    ...
    public void insertBatch(List<Vehicle> vehicles) {
        String sql = "INSERT INTO VEHICLE (VEHICLE_NO, COLOR, WHEEL, SEAT) "
                + "VALUES (:vehicleNo, :color, :wheel, :seat)";

        List<SqlParameterSource> parameters = new ArrayList<SqlParameterSource>();
        for (Vehicle vehicle : vehicles) {
            parameters.add(new BeanPropertySqlParameterSource(vehicle));
        }

        getSimpleJdbcTemplate().batchUpdate(sql,
                parameters.toArray(new SqlParameterSource[0]));
    }
}

Handling Exceptions in the Spring JDBC Framework

Problem

Many of the JDBC APIs declare throwing java.sql.SQLException, a checked exception that must be caught. It's very troublesome to handle this kind of exception every time you perform a database operation. You often have to define your own policy to handle this kind of exception. Failure to do so may lead to inconsistent exception handling.

Solution

The Spring framework offers a consistent data access exception-handling mechanism for its data access module, including the JDBC framework. In general, all exceptions thrown by the Spring JDBC framework are subclasses of org.springframework.dao.DataAccessException, a type of RuntimeException that you are not forced to catch. It's the root exception class for all exceptions in Spring's data access module.

Figure 15-1 shows only part of the DataAccessException hierarchy in Spring's data access module. In total, there are more than 30 exception classes defined for different categories of data access exceptions.

Common exception classes in the DataAccessException hierarchy

Figure 15.1. Common exception classes in the DataAccessException hierarchy

How It Works

Understanding Exception Handling in the Spring JDBC Framework

Until now, you haven't handled JDBC exceptions explicitly when using a JDBC template or JDBC operation objects. To help you understand the Spring JDBC framework's exception-handling mechanism, let's consider the following code fragment in the Main class, which inserts a vehicle. What happens if you insert a vehicle with a duplicate vehicle number?

package com.apress.springrecipes.vehicle;
...
public class Main {

    public static void main(String[] args) {
        ...
        VehicleDao vehicleDao = (VehicleDao) context.getBean("vehicleDao");
        Vehicle vehicle = new Vehicle("EX0001", "Green", 4, 4);
        vehicleDao.insert(vehicle);
    }
}

If you run the method twice, or the vehicle has already been inserted into the database, it will throw a DuplicateKeyException, an indirect subclass of DataAccessException. In your DAO methods, you neither need to surround the code with a try/catch block nor declare throwing an exception in the method signature. This is because DataAccessException (and therefore its subclasses, including DuplicateKeyException) is an unchecked exception that you are not forced to catch. The direct parent class of DataAccessException is NestedRuntimeException, a core Spring exception class that wraps another exception in a RuntimeException.

When you use the classes of the Spring JDBC framework, they will catch SQLException for you and wrap it with one of the subclasses of DataAccessException. As this exception is a RuntimeException, you are not required to catch it.

But how does the Spring JDBC framework know which concrete exception in the DataAccessException hierarchy should be thrown? It's by looking at the errorCode and SQLState properties of the caught SQLException. As a DataAccessException wraps the underlying SQLException as the root cause, you can inspect the errorCode and SQLState properties with the following catch block:

package com.apress.springrecipes.vehicle;
...
import java.sql.SQLException;

import org.springframework.dao.DataAccessException;

public class Main {

    public static void main(String[] args) {
        ...
        VehicleDao vehicleDao = (VehicleDao) context.getBean("vehicleDao");
        Vehicle vehicle = new Vehicle("EX0001", "Green", 4, 4);
        try {
            vehicleDao.insert(vehicle);
        } catch (DataAccessException e) {
            SQLException sqle = (SQLException) e.getCause();
            System.out.println("Error code: " + sqle.getErrorCode());
            System.out.println("SQL state: " + sqle.getSQLState());
        }
    }
}

When you insert the duplicate vehicle again, notice that Apache Derby returns the following error code and SQL state:

Error code : −1

SQL state : 23505

If you refer to the Apache Derby reference manual, you will find the error code description shown in Table 15-2.

Table 15.3. Apache Derby's Error Code Description

SQL State

Message Text

23505

The statement was aborted because it would have caused a duplicate key value in a unique or primary key constraint or unique index identified by '<value>' defined on '<value>'.

How does the Spring JDBC framework know that state 23505 should be mapped to DuplicateKeyException? The error code and SQL state are database specific, which means different database products may return different codes for the same kind of error. Moreover, some database products will specify the error in the errorCode property, while others (like Derby) will do so in the SQLState property.

As an open Java application framework, Spring understands the error codes of most popular database products. Because of the large number of error codes, however, it can only maintain mappings for the most frequently encountered errors. The mapping is defined in the sql-error-codes.xml file, located in the org.springframework.jdbc.support package. The following snippet for Apache Derby is taken from this file:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 3.0//EN"
    "http://www.springframework.org/dtd/spring-beans-3.0.dtd">

<beans>
    ...

<bean id="Derby" class="org.springframework.jdbc.support.SQLErrorCodes">
                             <property name="databaseProductName">
                                           <value>Apache Derby</value>
                             </property>
                             <property name="useSqlStateForTranslation">
                                           <value>true</value>
                             </property>
                             <property name="badSqlGrammarCodes">
               <value>42802,42821,42X01,42X02,42X03,42X04,42X05,42X06,
Apache Derby's Error Code Description
42X07,42X08</value> </property> <property name="duplicateKeyCodes"> <value>23505</value> </property>
<property name="dataIntegrityViolationCodes">
                                           <value>22001,22005,23502,23503,23513,
Apache Derby's Error Code Description
X0Y32</value> </property> <property name="dataAccessResourceFailureCodes"> <value>04501,08004,42Y07</value> </property> <property name="cannotAcquireLockCodes"> <value>40XL1</value> </property> <property name="deadlockLoserCodes"> <value>40001</value> </property> </bean> </beans>

Note that the databaseProductName property is used to match the database product name returned by Connection.getMetaData().getDatabaseProductName(). This enables Spring to know which type of database is currently connecting. The useSqlStateForTranslation property means that the SQLState property, rather than the errorCode property, should be used to match the error code. Finally, the SQLErrorCodes class defines several categories for you to map database error codes. The code 23505 lies in the dataIntegrityViolationCodes cdataIntegrityViolationCodes category.

Customizing Data Access Exception Handling

The Spring JDBC framework only maps well-known error codes. Sometimes, you may wish to customize the mapping yourself. For example, you might decide to add more codes to an existing category or define a custom exception for particular error codes.

In Table 15-2, the error code 23505 indicates a duplicate key error in Apache Derby. It is mapped by default to DataIntegrityViolationException. Suppose that you want to create a custom exception type, MyDuplicateKeyException, for this kind of error. It should extend DataIntegrityViolationException because it is also a kind of data integrity violation error. Remember that for an exception to be thrown by the Spring JDBC framework, it must be compatible with the root exception class DataAccessException.

package com.apress.springrecipes.vehicle;

import org.springframework.dao.DataIntegrityViolationException;

public class MyDuplicateKeyException extends DataIntegrityViolationException {

    public MyDuplicateKeyException(String msg) {
        super(msg);
    }

    public MyDuplicateKeyException(String msg, Throwable cause) {
        super(msg, cause);
    }
}

By default, Spring will look up an exception from the sql-error-codes.xml file located in the org.springframework.jdbc.support package. However, you can override some of the mappings by providing a file with the same name in the root of the classpath. If Spring can find your custom file, it will look up an exception from your mapping first. However, if it does not find a suitable exception there, Spring will look up the default mapping.

For example, suppose that you want to map your custom DuplicateKeyException type to error code 23505. You have to add the binding via a CustomSQLErrorCodesTranslation bean, and then add this bean to the customTranslations category.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN"
    "http://www.springframework.org/dtd/spring-beans-2.0.dtd">

<beans>
    <bean id="Derby"
        class="org.springframework.jdbc.support.SQLErrorCodes">
        <property name="databaseProductName">
            <value>Apache Derby</value>
        </property>
        <property name="useSqlStateForTranslation">
            <value>true</value>
        </property>
        <property name="customTranslations">
            <list>
                <ref local="myDuplicateKeyTranslation" />
            </list>
        </property>
    </bean>

    <bean id="myDuplicateKeyTranslation"
        class="org.springframework.jdbc.support.CustomSQLErrorCodesTranslation">
        <property name="errorCodes">
            <value>23505</value>
        </property>
        <property name="exceptionClass">
            <value>
                com.apress.springrecipes.vehicle.MyDuplicateKeyException
            </value>
        </property>
    </bean>
</beans>

Now, if you remove the try/catch block surrounding the vehicle insert operation and insert a duplicate vehicle, the Spring JDBC framework will throw a MyDuplicateKeyException instead.

However, if you are not satisfied with the basic code-to-exception mapping strategy used by the SQLErrorCodes class, you may further implement the SQLExceptionTranslator interface and inject its instance into a JDBC template via the setExceptionTranslator() method.

Problems with Using ORM Frameworks Directly

Problem

You've decided to go to the next level—you have a sufficiently complex domain model, and manually writing all the code for each entity is getting tedious, so you begin to investigate a few alternatives, like Hibernate. You're stunned to find that while they're powerful, they can be anything but simple!

Solution

Let Spring lend a hand; it has facilities for dealing with ORM layers that rival those available for plain ol' JDBC access.

How It Works

Suppose you are developing a course management system for a training center. The first class you create for this system is Course. This class is called an entity class or a persistent class because it represents a real-world entity and its instances will be persisted to a database. Remember that for each entity class to be persisted by an ORM framework, a default constructor with no argument is required.

package com.apress.springrecipes.course;
...
public class Course {

    private Long id;
    private String title;
    private Date beginDate;
    private Date endDate;
    private int fee;

    // Constructors, Getters and Setters
    ...
}

For each entity class, you must define an identifier property to uniquely identify an entity. It's a best practice to define an auto-generated identifier because this has no business meaning and thus won't be changed under any circumstances. Moreover, this identifier will be used by the ORM framework to determine an entity's state. If the identifier value is null, this entity will be treated as a new and unsaved entity. When this entity is persisted, an insert SQL statement will be issued; otherwise, an update statement will. To allow the identifier to be null, you should choose a primitive wrapper type like java.lang.Integer and java.lang.Long for the identifier.

In your course management system, you need a DAO interface to encapsulate the data access logic. Let's define the following operations in the CourseDao interface:

package com.apress.springrecipes.course;
...
public interface CourseDao {

    public void store(Course course);
    public void delete(Long courseId);
    public Course findById(Long courseId);
    public List<Course> findAll();
}

Usually, when using ORM for persisting objects, the insert and update operations are combined into a single operation (e.g., store). This is to let the ORM framework (not you) decide whether an object should be inserted or updated.

In order for an ORM framework to persist your objects to a database, it must know the mapping metadata for the entity classes. You have to provide mapping metadata to it in its supported format. The native format for Hibernate is XML. However, because each ORM framework may have its own format for defining mapping metadata, JPA defines a set of persistent annotations for you to define mapping metadata in a standard format that is more likely to be reusable in other ORM frameworks.

Hibernate also supports the use of JPA annotations to define mapping metadata, so there are essentially three different strategies for mapping and persisting your objects with Hibernate and JPA:

  • Using the Hibernate API to persist objects with Hibernate XML mappings

  • Using the Hibernate API to persist objects with JPA annotations

  • Using JPA to persist objects with JPA annotations

The core programming elements of Hibernate, JPA, and other ORM frameworks resemble those of JDBC. They are summarized in Table 15-3.

Table 15.4. Core Programming Elements for Different Data Access Strategies

Concept

JDBC

Hibernate

JPA

Resource

Connection

Session

EntityManager

Resource factory

DataSource

SessionFactory

EntityManagerFactory

Exception

SQLException

HibernateException

PersistenceException

In Hibernate, the core interface for object persistence is Session, whose instances can be obtained from a SessionFactory instance. In JPA, the corresponding interface is EntityManager, whose instances can be obtained from an EntityManagerFactory instance. The exceptions thrown by Hibernate are of type HibernateException, while those thrown by JPA may be of type PersistenceException or other Java SE exceptions like IllegalArgumentException and IllegalStateException. Note that all these exceptions are subclasses of RuntimeException, which you are not forced to catch and handle.

Persisting Objects Using the Hibernate API with Hibernate XML Mappings

To map entity classes with Hibernate XML mappings, you can provide a single mapping file for each class or a large file for several classes. Practically, you should define one for each class by joining the class name with .hbm.xml as the file extension for ease of maintenance. The middle extension hbm stands for "Hibernate metadata."

The mapping file for the Course class should be named Course.hbm.xml and put in the same package as the entity class.

<!DOCTYPE hibernate-mapping
    PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="com.apress.springrecipes.course">
    <class name="Course" table="COURSE">
        <id name="id" type="long" column="ID">
            <generator class="identity" />
        </id>
        <property name="title" type="string">
            <column name="TITLE" length="100" not-null="true" />
        </property>
        <property name="beginDate" type="date" column="BEGIN_DATE" />
        <property name="endDate" type="date" column="END_DATE" />
        <property name="fee" type="int" column="FEE" />
    </class>
</hibernate-mapping>

In the mapping file, you can specify a table name for this entity class and a table column for each simple property. You can also specify the column details such as column length, not-null constraints, and unique constraints. In addition, each entity must have an identifier defined, which can be generated automatically or assigned manually. In this example, the identifier will be generated using a table identity column.

Each application that uses Hibernate requires a global configuration file to configure properties such as the database settings (either JDBC connection properties or a data source's JNDI name), the database dialect, the mapping metadata's locations, and so on. When using XML mapping files to define mapping metadata, you have to specify the locations of the XML files. By default, Hibernate will read the hibernate.cfg.xml file from the root of the classpath. The middle extension cfg stands for "configuration." If there is a hibernate.properties file on the classpath, that file will be consulted first and overridden by hibernate.cfg.xml.

<!DOCTYPE hibernate-configuration PUBLIC
    "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
    <session-factory>
        <property name="connection.driver_class">
            org.apache.derby.jdbc.ClientDriver
        </property>
<property name="connection.url">
            jdbc:derby://localhost:1527/course;create=true
        </property>
        <property name="connection.username">app</property>
        <property name="connection.password">app</property>
        <property name="dialect">org.hibernate.dialect.DerbyDialect</property>
        <property name="show_sql">true</property>
        <property name="hbm2ddl.auto">update</property>

        <mapping resource="com/apress/springrecipes/course/
Persisting Objects Using the Hibernate API with Hibernate XML Mappings
Course.hbm.xml" /> </session-factory> </hibernate-configuration>

Before you can persist your objects, you have to create tables in a database schema to store the object data. When using an ORM framework like Hibernate, you usually needn't design the tables by yourself. If you set the hbm2ddl.auto property to update, Hibernate can help you to update the database schema and create the tables when necessary. Naturally, you shouldn't enable this in production, but it can be a great speed boost for development.

Now, let's implement the DAO interface in the hibernate subpackage using the plain Hibernate API. Before you call the Hibernate API for object persistence, you have to initialize a Hibernate session factory (e.g., in the constructor).

package com.apress.springrecipes.course.hibernate;
...
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;

public class HibernateCourseDao implements CourseDao {

    private SessionFactory sessionFactory;

    public HibernateCourseDao() {
        Configuration configuration = new Configuration().configure();
        sessionFactory = configuration.buildSessionFactory();
    }

    public void store(Course course) {
        Session session = sessionFactory.openSession();
        Transaction tx = session.getTransaction();
        try {
            tx.begin();
            session.saveOrUpdate(course);
            tx.commit();
        } catch (RuntimeException e) {
            tx.rollback();
            throw e;
} finally {
            session.close();
        }
    }

    public void delete(Long courseId) {
        Session session = sessionFactory.openSession();
        Transaction tx = session.getTransaction();
        try {
            tx.begin();
            Course course = (Course) session.get(Course.class, courseId);
            session.delete(course);
            tx.commit();
        } catch (RuntimeException e) {
            tx.rollback();
            throw e;
        } finally {
            session.close();
        }
    }

    public Course findById(Long courseId) {
        Session session = sessionFactory.openSession();
        try {
            return (Course) session.get(Course.class, courseId);
        } finally {
            session.close();
        }
    }

    public List<Course> findAll() {
        Session session = sessionFactory.openSession();
        try {
            Query query = session.createQuery("from Course");
            return query.list();
        } finally {
            session.close();
        }
    }
}

The first step in using Hibernate is to create a Configuration object and ask it to load the Hibernate configuration file. By default, it loads hibernate.cfg.xml from the classpath root when you call the configure() method. Then, you build a Hibernate session factory from this Configuration object. The purpose of a session factory is to produce sessions for you to persist your objects.

In the preceding DAO methods, you first open a session from the session factory. For any operation that involves database update, such as saveOrUpdate() and delete(), you must start a Hibernate transaction on that session. If the operation completes successfully, you commit the transaction. Otherwise, you roll it back if any RuntimeException happens. For read-only operations such as get() and HQL queries, there's no need to start a transaction. Finally, you must remember to close a session to release the resources held by this session.

You can create the following Main class to test run all the DAO methods. It also demonstrates an entity's typical life cycle.

package com.apress.springrecipes.course;
...
public class Main {

    public static void main(String[] args) {
        CourseDao courseDao = new HibernateCourseDao();

        Course course = new Course();
        course.setTitle("Core Spring");
        course.setBeginDate(new GregorianCalendar(2007, 8, 1).getTime());
        course.setEndDate(new GregorianCalendar(2007, 9, 1).getTime());
        course.setFee(1000);
        courseDao.store(course);

        List<Course> courses = courseDao.findAll();
        Long courseId = courses.get(0).getId();

        course = courseDao.findById(courseId);
        System.out.println("Course Title: " + course.getTitle());
        System.out.println("Begin Date: " + course.getBeginDate());
        System.out.println("End Date: " + course.getEndDate());
        System.out.println("Fee: " + course.getFee());

        courseDao.delete(courseId);
    }
}

Persisting Objects Using the Hibernate API with JPA Annotations

JPA annotations are standardized in the JSR-220 specification, so they're supported by all JPA-compliant ORM frameworks, including Hibernate. Moreover, the use of annotations will be more convenient for you to edit mapping metadata in the same source file.

The following Course class illustrates the use of JPA annotations to define mapping metadata:

package com.apress.springrecipes.course;
...
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 = "COURSE")
public class Course {

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

    @Column(name = "TITLE", length = 100, nullable = false)
    private String title;

    @Column(name = "BEGIN_DATE")
    private Date beginDate;

    @Column(name = "END_DATE")
    private Date endDate;

    @Column(name = "FEE")
    private int fee;

    // Constructors, Getters and Setters
    ...
}

Each entity class must be annotated with the @Entity annotation. You can assign a table name for an entity class in this annotation. For each property, you can specify a column name and column details using the @Column annotation. Each entity class must have an identifier defined by the @Id annotation. You can choose a strategy for identifier generation using the @GeneratedValue annotation. Here, the identifier will be generated by a table identity column.

Hibernate supports both native XML mapping files and JPA annotations as ways of defining mapping metadata. For JPA annotations, you have to specify the fully qualified names of the entity classes in hibernate.cfg.xml for Hibernate to read the annotations.

<hibernate-configuration>
    <session-factory>
        ...
        <!-- For Hibernate XML mappings -->
        <!--
        <mapping resource="com/apress/springrecipes/course/
Persisting Objects Using the Hibernate API with JPA Annotations
Course.hbm.xml" /> --> <!-- For JPA annotations --> <mapping class="com.apress.springrecipes.course.Course" /> </session-factory> </hibernate-configuration>

In the Hibernate DAO implementation, the Configuration class you used is for reading XML mappings. If you use JPA annotations to define mapping metadata for Hibernate, you have to use its subclass, AnnotationConfiguration, instead.

package com.apress.springrecipes.course.hibernate;
...
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;

public class HibernateCourseDao implements CourseDao {

    private SessionFactory sessionFactory;

    public HibernateCourseDao() {
        // For Hibernate XML mapping
        // Configuration configuration = new Configuration().configure();

        // For JPA annotation
        Configuration configuration = new AnnotationConfiguration().configure();

        sessionFactory = configuration.buildSessionFactory();
    }
    ...
}

Persisting Objects Using JPA with Hibernate as the Engine

In addition to persistent annotations, JPA defines a set of programming interfaces for object persistence. However, JPA is not a persistence implementation; you have to pick up a JPA-compliant engine to provide persistence services. Hibernate can be JPA-compliant through the Hibernate EntityManager extension module. With this extension, Hibernate can work as an underlying JPA engine to persist objects. This lets you retain both the valuable investment in Hibernate (perhaps it's faster or handles certain operations more to your satisfaction) and write code that is JPA-compliant and portable among other JPA engines. This can also be a useful way to transition a code base to JPA. New code is written strictly against the JPA APIs, and older code is transitioned to the JPA interfaces.

In a Java EE environment, you can configure the JPA engine in a Java EE container. But in a Java SE application, you have to set up the engine locally. The configuration of JPA is through the central XML file persistence.xml, located in the META-INF directory of the classpath root. In this file, you can set any vendor-specific properties for the underlying engine configuration.

Now, let's create the JPA configuration file persistence.xml in the META-INF directory of the classpath root. Each JPA configuration file contains one or more <persistence-unit> elements. A persistence unit defines a set of persistent classes and how they should be persisted. Each persistence unit requires a name for identification. Here, you assign the name course to this persistence unit.

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
        http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
    version="1.0">
<persistence-unit name="course">
        <properties>
            <property name="hibernate.ejb.cfgfile" value="/hibernate.cfg.xml" />
        </properties>
    </persistence-unit>
</persistence>

In this JPA configuration file, you configure Hibernate as your underlying JPA engine by referring to the Hibernate configuration file located in the classpath root. However, because Hibernate EntityManager will automatically detect XML mapping files and JPA annotations as mapping metadata, you have no need to specify them explicitly. Otherwise, you will encounter an org.hibernate.DuplicateMappingException.

<hibernate-configuration>
    <session-factory>
        ...
        <!-- Don't need to specify mapping files and annotated classes -->
        <!--
        <mapping resource="com/apress/springrecipes/course/
Persisting Objects Using JPA with Hibernate as the Engine
Course.hbm.xml" /> <mapping class="com.apress.springrecipes.course.Course" /> --> </session-factory> </hibernate-configuration>

As an alternative to referring to the Hibernate configuration file, you can also centralize all the Hibernate configurations in persistence.xml.

<persistence ...>
    <persistence-unit name="course">
        <properties>
            <property name="hibernate.connection.driver_class"
                value="org.apache.derby.jdbc.ClientDriver" />
            <property name="hibernate.connection.url"
                value="jdbc:derby://localhost:1527/course;create=true" />
            <property name="hibernate.connection.username" value="app" />
            <property name="hibernate.connection.password" value="app" />
            <property name="hibernate.dialect"
                value="org.hibernate.dialect.DerbyDialect" />
            <property name="hibernate.show_sql" value="true" />
            <property name="hibernate.hbm2ddl.auto" value="update" />
        </properties>
    </persistence-unit>
</persistence>'

In a Java EE environment, a Java EE container is able to manage the entity manager for you and inject it into your EJB components directly. But when you use JPA outside of a Java EE container (e.g., in a Java SE application), you have to create and maintain the entity manager by yourself.

Note

To use Hibernate as the underlying JPA engine, you have to include the Hibernate Entity Manager libraries to your CLASSPATH. If you're using Maven, add the following dependency to your project:

<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-entitymanager</artifactId>
  <version>${hibernate.version}</version>
 </dependency>

Now, let's implement the CourseDao interface in the jpa subpackage using JPA in a Java SE application. Before you call JPA for object persistence, you have to initialize an entity manager factory. The purpose of an entity manager factory is to produce entity managers for you to persist your objects.

package com.apress.springrecipes.course.jpa;
...
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import javax.persistence.Query;

public class JpaCourseDao implements CourseDao {

    private EntityManagerFactory entityManagerFactory;

    public JpaCourseDao() {
        entityManagerFactory = Persistence.createEntityManagerFactory("course");
    }

    public void store(Course course) {
        EntityManager manager = entityManagerFactory.createEntityManager();
        EntityTransaction tx = manager.getTransaction();
        try {
            tx.begin();
            manager.merge(course);
            tx.commit();
        } catch (RuntimeException e) {
            tx.rollback();
            throw e;
        } finally {
            manager.close();
        }
    }
public void delete(Long courseId) {
        EntityManager manager = entityManagerFactory.createEntityManager();
        EntityTransaction tx = manager.getTransaction();
        try {
            tx.begin();
            Course course = manager.find(Course.class, courseId);
            manager.remove(course);
            tx.commit();
        } catch (RuntimeException e) {
            tx.rollback();
            throw e;
        } finally {
            manager.close();
        }
    }

    public Course findById(Long courseId) {
        EntityManager manager = entityManagerFactory.createEntityManager();
        try {
            return manager.find(Course.class, courseId);
        } finally {
            manager.close();
        }
    }

    public List<Course> findAll() {
        EntityManager manager = entityManagerFactory.createEntityManager();
        try {
            Query query = manager.createQuery(
                "select course from Course course");
            return query.getResultList();
        } finally {
            manager.close();
        }
    }
}

The entity manager factory is built by the static method createEntityManagerFactory() of the javax.persistence.Persistence class. You have to pass in a persistence unit name defined in persistence.xml for an entity manager factory.

In the preceding DAO methods, you first create an entity manager from the entity manager factory. For any operation that involves database update, such as merge() and remove(), you must start a JPA transaction on the entity manager. For read-only operations such as find() and JPA queries, there's no need to start a transaction. Finally, you must close an entity manager to release the resources.

You can test this DAO with the similar Main class, but this time, you instantiate the JPA DAO implementation instead.

package com.apress.springrecipes.course;
...
public class Main {

    public static void main(String[] args) {
        CourseDao courseDao = new JpaCourseDao();
        ...
    }
}

In the preceding DAO implementations for both Hibernate and JPA, there are only one or two lines that are different for each DAO method. The rest of the lines are boilerplate routine tasks that you have to repeat. Moreover, each ORM framework has its own API for local transaction management.

Configuring ORM Resource Factories in Spring

Problem

When using an ORM framework on its own, you have to configure its resource factory with its API. For Hibernate and JPA, you have to build a session factory and an entity manager factory from the native Hibernate API and JPA. You have no choice but to manage these objects manually, without Spring's support.

Solution

Spring provides several factory beans for you to create a Hibernate session factory or a JPA entity manager factory as a singleton bean in the IoC container. These factories can be shared between multiple beans via dependency injection. Moreover, this allows the session factory and the entity manager factory to integrate with other Spring data access facilities, such as data sources and transaction managers.

How It Works

Configuring a Hibernate Session Factory in Spring

First of all, let's modify HibernateCourseDao to accept a session factory via dependency injection, instead of creating it directly with the native Hibernate API in the constructor.

package com.apress.springrecipes.course.hibernate;
...
import org.hibernate.SessionFactory;

public class HibernateCourseDao implements CourseDao {
private SessionFactory sessionFactory;

    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }
    ...
}

Now, let's look at how to declare a session factory that uses XML mapping files in Spring. For this purpose, you have to enable the XML mapping file definition in hibernate.cfg.xml again.

<hibernate-configuration>
    <session-factory>
        ...
        <!-- For Hibernate XML mappings -->
        <mapping resource="com/apress/springrecipes/course/
Configuring a Hibernate Session Factory in Spring
Course.hbm.xml" /> </session-factory> </hibernate-configuration>

Then, you create a bean configuration file for using Hibernate as the ORM framework (e.g., beans-hibernate.xml in the classpath root). You can declare a session factory that uses XML mapping files with the factory bean LocalSessionFactoryBean. You can also declare a HibernateCourseDao instance under Spring's management.

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

    <bean id="sessionFactory"
        class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <property name="configLocation" value="classpath:hibernate.cfg.xml" />
    </bean>

    <bean id="courseDao"
        class="com.apress.springrecipes.course.hibernate. 
Configuring a Hibernate Session Factory in Spring
HibernateCourseDao"> <property name="sessionFactory" ref="sessionFactory" /> </bean> </beans>

Note that you can specify the configLocation property for this factory bean to load the Hibernate configuration file. The configLocation property is of type Resource, but you can assign a string value to it. The built-in property editor ResourceEditor will convert it into a Resource object. The preceding factory bean loads the configuration file from the root of the classpath.

Now, you can modify the Main class to retrieve the HibernateCourseDao instance from the Spring IoC container.

package com.apress.springrecipes.course;
...
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {

    public static void main(String[] args) {
        ApplicationContext context =
            new ClassPathXmlApplicationContext("beans-hibernate.xml");

        CourseDao courseDao = (CourseDao) context.getBean("courseDao");
        ...
    }
}

The preceding factory bean creates a session factory by loading the Hibernate configuration file, which includes the database settings (either JDBC connection properties or a data source's JNDI name). Now, suppose that you have a data source defined in the Spring IoC container. If you want to use this data source for your session factory, you can inject it into the dataSource property of LocalSessionFactoryBean. The data source specified in this property will override the database settings in the Hibernate configuration file. If this is set, the Hibernate settings should not define a connection provider to avoid meaningless double configuration.

<beans ...>
    ...
    <bean id="dataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName"
            value="org.apache.derby.jdbc.ClientDriver" />
        <property name="url"
            value="jdbc:derby://localhost:1527/course;create=true" />
        <property name="username" value="app" />
        <property name="password" value="app" />
    </bean>

    <bean id="sessionFactory"
        class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="configLocation" value="classpath:hibernate.cfg.xml" />
    </bean>
</beans>

Or you can even ignore the Hibernate configuration file by merging all the configurations into LocalSessionFactoryBean. For example, you can specify the locations of the XML mapping files in the mappingResources property and other Hibernate properties such as the database dialect in the hibernateProperties property.

<bean id="sessionFactory"
    class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="mappingResources">
        <list>
            <value>com/apress/springrecipes/course/Course.hbm.xml</value>
        </list>
    </property>
    <property name="hibernateProperties">
        <props>
            <prop key="hibernate.dialect">org.hibernate.dialect.DerbyDialect</prop>
            <prop key="hibernate.show_sql">true</prop>
            <prop key="hibernate.hbm2ddl.auto">update</prop>
        </props>
    </property>
</bean>

The mappingResources property's type is String[], so you can specify a set of mapping files in the classpath. LocalSessionFactoryBean also allows you take advantage of Spring's resource-loading support to load mapping files from various types of locations. You can specify the resource paths of the mapping files in the mappingLocations property, whose type is Resource[].

<bean id="sessionFactory"
    class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    ...
    <property name="mappingLocations">
        <list>
            <value>classpath:com/apress/springrecipes/course/Course.hbm.xml</value>
        </list>
    </property>
    ...
</bean>

With Spring's resource-loading support, you can also use wildcards in a resource path to match multiple mapping files so that you don't need to configure their locations every time you add a new entity class. Spring's preregistered ResourceArrayPropertyEditor will convert this path into a Resource array.

<bean id="sessionFactory"
    class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    ...
    <property name="mappingLocations"
        value="classpath:com/apress/springrecipes/course/*.hbm.xml" />
    ...
</bean>

If your mapping metadata is provided through JPA annotations, you have to make use of AnnotationSessionFactoryBean instead. You have to specify the persistent classes in the annotatedClasses property of AnnotationSessionFactoryBean, or use the packagesToScan property to tell the AnnotationSessionFactoryBean which packages to scan for beans with JPA annotations.

<bean id="sessionFactory" class="org.springframework.orm.hibernate3.
Configuring a Hibernate Session Factory in Spring
annotation.AnnotationSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="annotatedClasses"> <list> <value>com.apress.springrecipes.course.Course</value> </list> </property> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.DerbyDialect</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.hbm2ddl.auto">update</prop> </props> </property> </bean>

Now you can delete the Hibernate configuration file (i.e., hibernate.cfg.xml) because its configurations have been ported to Spring.

Configuring a JPA Entity Manager Factory in Spring

First of all, let's modify JpaCourseDao to accept an entity manager factory via dependency injection, instead of creating it directly in the constructor.

package com.apress.springrecipes.course.jpa;
...
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public class JpaCourseDao implements CourseDao {

    private EntityManagerFactory entityManagerFactory;

    public void setEntityManagerFactory(
            EntityManagerFactory entityManagerFactory) {
        this.entityManagerFactory = entityManagerFactory;
    }
    ...
}

The JPA specification defines how you should obtain an entity manager factory in Java SE and Java EE environments. In a Java SE environment, an entity manager factory is created manually by calling the createEntityManagerFactory() static method of the Persistence class.

Let's create a bean configuration file for using JPA (e.g., beans-jpa.xml in the classpath root). Spring provides a factory bean, LocalEntityManagerFactoryBean, for you to create an entity manager factory in the IoC container. You must specify the persistence unit name defined in the JPA configuration file. You can also declare a JpaCourseDao instance under Spring's management.

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

    <bean id="entityManagerFactory"
        class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
        <property name="persistenceUnitName" value="course" />
    </bean>

   <bean id="courseDao"
        class="com.apress.springrecipes.course.jpa.JpaCourseDao">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>
</beans>

Now, you can test this JpaCourseDao instance with the Main class by retrieving it from the Spring IoC container.

package com.apress.springrecipes.course;
...
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {

    public static void main(String[] args) {
        ApplicationContext context =
            new ClassPathXmlApplicationContext("beans-jpa.xml");

        CourseDao courseDao = (CourseDao) context.getBean("courseDao");
        ...
    }
}

In a Java EE environment, you can look up an entity manager factory from a Java EE container with JNDI. In Spring, you can perform a JNDI lookup by using the <jee:jndi-lookup> element.

<jee:jndi-lookup id="entityManagerFactory" jndi-name="jpa/coursePU" />

LocalEntityManagerFactoryBean creates an entity manager factory by loading the JPA configuration file (i.e., persistence.xml). Spring supports a more flexible way to create an entity manager factory by another factory bean, LocalContainerEntityManagerFactoryBean. It allows you to override some of the configurations in the JPA configuration file, such as the data source and database dialect. So, you can take advantage of Spring's data access facilities to configure the entity manager factory.

<beans ...>
    ...
    <bean id="dataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName"
            value="org.apache.derby.jdbc.ClientDriver" />
        <property name="url"
            value="jdbc:derby://localhost:1527/course;create=true" />
        <property name="username" value="app" />
        <property name="password" value="app" />
    </bean>

    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.
        LocalContainerEntityManagerFactoryBean">
        <property name="persistenceUnitName" value="course" />
        <property name="dataSource" ref="dataSource" />
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.
Configuring a JPA Entity Manager Factory in Spring
HibernateJpaVendorAdapter"> <property name="databasePlatform" value="org.hibernate.dialect.DerbyDialect" /> <property name="showSql" value="true" /> <property name="generateDdl" value="true" /> </bean> </property> </bean> </beans>

In the preceding bean configurations, you inject a data source into this entity manager factory. It will override the database settings in the JPA configuration file. You can set a JPA vendor adapter to LocalContainerEntityManagerFactoryBean to specify JPA engine–specific properties. With Hibernate as the underlying JPA engine, you should choose HibernateJpaVendorAdapter. Other properties that are not supported by this adapter can be specified in the jpaProperties property.

Now your JPA configuration file (i.e., persistence.xml) can be simplified as follows because its configurations have been ported to Spring:

<persistence ...>
    <persistence-unit name="course" />
</persistence>

Persisting Objects with Spring's ORM Templates

Problem

When using an ORM framework on its own, you have to repeat certain routine tasks for each DAO operation. For example, in a DAO operation implemented with Hibernate or JPA, you have to open and close a session or an entity manager, and begin, commit, and roll back a transaction with the native API.

Solution

Spring's approach to simplifying an ORM framework's usage is the same as JDBC's—by defining template classes and DAO support classes. Also, Spring defines an abstract layer on top of different transaction management APIs. For different ORM frameworks, you only have to pick up a corresponding transaction manager implementation. Then, you can manage transactions for them in a similar way.

In Spring's data access module, the support for different data access strategies is consistent. Table 15-4 compares the support classes for JDBC, Hibernate, and JPA.

Table 15.5. Spring's Support Classes for Different Data Access Strategies

Support Class

JDBC

Hibernate

JPA

Template class

JdbcTemplate

HibernateTemplate

JpaTemplate

DAO support

JdbcDaoSupport

HibernateDaoSupport

JpaDaoSupport

Transaction

DataSourceTransaction

HibernateTransaction

JpaTransactionManager

Spring defines the HibernateTemplate and JpaTemplate classes to provide template methods for different types of Hibernate and JPA operations to minimize the effort involved in using them. The template methods in HibernateTemplate and JpaTemplate ensure that Hibernate sessions and JPA entity managers will be opened and closed properly. They will also have native Hibernate and JPA transactions participate in Spring-managed transactions. As a result, you will be able to manage transactions declaratively for your Hibernate and JPA DAOs without any boilerplate transaction code.

How It Works

Using a Hibernate Template and a JPA Template

First, the HibernateCourseDao class can be simplified as follows with the help of Spring's HibernateTemplate:

package com.apress.springrecipes.course.hibernate;
...
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.transaction.annotation.Transactional;

public class HibernateCourseDao implements CourseDao {

    private HibernateTemplate hibernateTemplate;

    public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
        this.hibernateTemplate = hibernateTemplate;
    }
@Transactional
    public void store(Course course) {
        hibernateTemplate.saveOrUpdate(course);
    }

    @Transactional
    public void delete(Long courseId) {
        Course course = (Course) hibernateTemplate.get(Course.class, courseId);
        hibernateTemplate.delete(course);
    }

    @Transactional(readOnly = true)
    public Course findById(Long courseId) {
        return (Course) hibernateTemplate.get(Course.class, courseId);
    }

    @Transactional(readOnly = true)
    public List<Course> findAll() {
        return hibernateTemplate.find("from Course");
    }
}

In this DAO implementation, you declare all the DAO methods to be transactional with the @Transactional annotation. Among these methods, findById() and findAll() are read-only. The template methods in HibernateTemplate are responsible for managing the sessions and transactions. If there are multiple Hibernate operations in a transactional DAO method, the template methods will ensure that they will run within the same session and transaction. As a result, you have no need to deal with the Hibernate API for session and transaction management.

The HibernateTemplate class is thread-safe, so you can declare a single instance of it in the bean configuration file for Hibernate (i.e., beans-hibernate.xml) and inject this instance into all Hibernate DAOs. A HibernateTemplate instance requires the sessionFactory property to be set. You can inject this property by either setter method or constructor argument.

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="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">
    ...
    <tx:annotation-driven />

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

    <bean name="courseDao"
        class="com.apress.springrecipes.course.hibernate. 
Using a Hibernate Template and a JPA Template
HibernateCourseDao"> <property name="hibernateTemplate" ref="hibernateTemplate" /> </bean> </beans>

To enable declarative transaction management for the methods annotated with @Transactional, you have to enable the <tx:annotation-driven> element in your bean configuration file. By default, it will look for a transaction manager with the name transactionManager, so you have to declare a HibernateTransactionManager instance with that name. HibernateTransactionManager requires the session factory property to be set. It will manage transactions for sessions opened through this session factory.

Similarly, you can simplify the JpaCourseDao class as follows with the help of Spring's JpaTemplate. You also declare all the DAO methods to be transactional.

package com.apress.springrecipes.course.jpa;
...
import org.springframework.orm.jpa.JpaTemplate;
import org.springframework.transaction.annotation.Transactional;

public class JpaCourseDao implements CourseDao {

    private JpaTemplate jpaTemplate;

    public void setJpaTemplate(JpaTemplate jpaTemplate) {
        this.jpaTemplate = jpaTemplate;
    }

    @Transactional
    public void store(Course course) {
        jpaTemplate.merge(course);
    }

    @Transactional
    public void delete(Long courseId) {
        Course course = jpaTemplate.find(Course.class, courseId);
        jpaTemplate.remove(course);
    }

    @Transactional(readOnly = true)
    public Course findById(Long courseId) {
        return jpaTemplate.find(Course.class, courseId);
    }
@Transactional(readOnly = true)
    public List<Course> findAll() {
        return jpaTemplate.find("from Course");
    }
}

In the bean configuration file for JPA (i.e., beans-jpa.xml), you can declare a JpaTemplate instance and inject it into all JPA DAOs. Also, you have to declare a JpaTransactionManager instance for managing JPA transactions.

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="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">
    ...
    <tx:annotation-driven />

    <bean id="transactionManager"
        class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>

    <bean id="jpaTemplate"
        class="org.springframework.orm.jpa.JpaTemplate">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>

    <bean name="courseDao"
        class="com.apress.springrecipes.course.jpa.JpaCourseDao">
        <property name="jpaTemplate" ref="jpaTemplate" />
    </bean>
</beans>

Another advantage of HibernateTemplate and JpaTemplate is that they will translate native Hibernate and JPA exceptions into exceptions in Spring's DataAccessException hierarchy. This allows consistent exception handling for all the data access strategies in Spring. For instance, if a database constraint is violated when persisting an object, Hibernate will throw an org.hibernate.exception.ConstraintViolationException, while JPA will throw a javax.persistence.EntityExistsException. These exceptions will be translated by HibernateTemplate and JpaTemplate into DataIntegrityViolationException, which is a subclass of Spring's DataAccessException.

If you want to get access to the underlying Hibernate session or JPA entity manager in HibernateTemplate or JpaTemplate in order to perform native Hibernate or JPA operations, you can implement the HibernateCallback or JpaCallback interface and pass its instance to the execute() method of the template. This will give you a chance to use any implementation-specific features directly if there's not sufficient support already available from the template implementations.

hibernateTemplate.execute(new HibernateCallback() {
    public Object doInHibernate(Session session) throws HibernateException,
            SQLException {
       // ... anything you can imagine doing can be done here.
Using a Hibernate Template and a JPA Template
// Cache invalidation, for example... } }; jpaTemplate.execute(new JpaCallback() { public Object doInJpa(EntityManager em) throws PersistenceException { // ... anything you can imagine doing can be done here. } };

Extending the Hibernate and JPA DAO Support Classes

Your Hibernate DAO can extend HibernateDaoSupport to have the setSessionFactory() and setHibernateTemplate() methods inherited. Then, in your DAO methods, you can simply call the getHibernateTemplate() method to retrieve the template instance.

package com.apress.springrecipes.course.hibernate;
...
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.transaction.annotation.Transactional;

public class HibernateCourseDao extends HibernateDaoSupport implements
        CourseDao {

    @Transactional
    public void store(Course course) {
        getHibernateTemplate().saveOrUpdate(course);
    }

    @Transactional
    public void delete(Long courseId) {
        Course course = (Course) getHibernateTemplate().get(Course.class,
                courseId);
        getHibernateTemplate().delete(course);
    }

    @Transactional(readOnly = true)
    public Course findById(Long courseId) {
        return (Course) getHibernateTemplate().get(Course.class, courseId);
    }

    @Transactional(readOnly = true)
    public List<Course> findAll() {
        return getHibernateTemplate().find("from Course");
    }
}

Because HibernateCourseDao inherits the setSessionFactory() and setHibernateTemplate() methods, you can inject either of them into your DAO so that you can retrieve the HibernateTemplate instance. If you inject a session factory, you will be able to delete the HibernateTemplate declaration.

<bean name="courseDao"
    class="com.apress.springrecipes.course.hibernate.HibernateCourseDao">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>

Similarly, your JPA DAO can extend JpaDaoSupport to have setEntityManagerFactory() and setJpaTemplate() inherited. In your DAO methods, you can simply call the getJpaTemplate() method to retrieve the template instance. This instance will contain the pre-initialized EntityManagerFactory.

package com.apress.springrecipes.course.jpa;
...
import org.springframework.orm.jpa.support.JpaDaoSupport;
import org.springframework.transaction.annotation.Transactional;

public class JpaCourseDao extends JpaDaoSupport implements CourseDao {

    @Transactional
    public void store(Course course) {
        getJpaTemplate().merge(course);
    }

    @Transactional
    public void delete(Long courseId) {
        Course course = getJpaTemplate().find(Course.class, courseId);
        getJpaTemplate().remove(course);
    }

    @Transactional(readOnly = true)
    public Course findById(Long courseId) {
        return getJpaTemplate().find(Course.class, courseId);
    }

    @Transactional(readOnly = true)
    public List<Course> findAll() {
        return getJpaTemplate().find("from Course");
    }
}

Because JpaCourseDao inherits both setEntityManagerFactory() and setJpaTemplate(), you can inject either of them into your DAO. If you inject an entity manager factory, you will be able to delete the JpaTemplate he JpaTemplate declaration.

<bean name="courseDao"
    class="com.apress.springrecipes.course.jpa.JpaCourseDao">
    <property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>

Persisting Objects with Hibernate's Contextual Sessions

Problem

Spring's HibernateTemplate can simplify your DAO implementation by managing sessions and transactions for you. However, using HibernateTemplate means your DAO has to depend on Spring's API.

Solution

An alternative to Spring's HibernateTemplate is to use Hibernate's contextual sessions. In Hibernate 3, a session factory can manage contextual sessions for you and allows you to retrieve them by the getCurrentSession() method on org.hibernate.SessionFactory. Within a single transaction, you will get the same session for each getCurrentSession() method call. This ensures that there will be only one Hibernate session per transaction, so it works nicely with Spring's transaction management support.

How It Works

To use the contextual session approach, your DAO methods require access to the session factory, which can be injected via a setter method or a constructor argument. Then, in each DAO method, you get the contextual session from the session factory and use it for object persistence.

package com.apress.springrecipes.course.hibernate;
...
import org.hibernate.Query;
import org.hibernate.SessionFactory;
import org.springframework.transaction.annotation.Transactional;

public class HibernateCourseDao implements CourseDao {

    private SessionFactory sessionFactory;

    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    @Transactional
    public void store(Course course) {
        sessionFactory.getCurrentSession().saveOrUpdate(course);
    }
@Transactional
    public void delete(Long courseId) {
        Course course = (Course) sessionFactory.getCurrentSession().get(
                Course.class, courseId);
        sessionFactory.getCurrentSession().delete(course);
    }

    @Transactional(readOnly = true)
    public Course findById(Long courseId) {
        return (Course) sessionFactory.getCurrentSession().get(
                Course.class, courseId);
    }

    @Transactional(readOnly = true)
    public List<Course> findAll() {
        Query query = sessionFactory.getCurrentSession().createQuery(
                "from Course");
        return query.list();
    }
}

Note that all your DAO methods must be made transactional. This is required because Spring wraps the SessionFactory with a proxy that expects that Spring's transaction management is in play when methods on a session are made. It will attempt to find a transaction and then fail, complaining that no Hibernate session's been bound to the thread. You can achieve this by annotating each method or the entire class with @Transactional. This ensures that the persistence operations within a DAO method will be executed in the same transaction and hence by the same session. Moreover, if a service layer component's method calls multiple DAO methods, and it propagates its own transaction to these methods, then all these DAO methods will run within the same session as well.

In the bean configuration file for Hibernate (i.e., beans-hibernate.xml), you have to declare a HibernateTransactionManager instance for this application and enable declarative transaction management via <tx:annotation-driven>.

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="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">
    ...
    <tx:annotation-driven />

    <bean id="transactionManager"
        class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>
<bean name="courseDao"
        class="com.apress.springrecipes.course.hibernate.HibernateCourseDao">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>
</beans>

Remember that HibernateTemplate will translate the native Hibernate exceptions into exceptions in Spring's DataAccessException hierarchy. This allows consistent exception handling for different data access strategies in Spring. However, when calling the native methods on a Hibernate session, the exceptions thrown will be of native type HibernateException. If you want the Hibernate exceptions to be translated into Spring's DataAccessException for consistent exception handling, you have to apply the @Repository annotation to your DAO class that requires exception translation.

package com.apress.springrecipes.course.hibernate;
...
import org.springframework.stereotype.Repository;

@Repository
public class HibernateCourseDao implements CourseDao {
    ...
}

Then, register a PersistenceExceptionTranslationPostProcessor instance to translate the native Hibernate exceptions into data access exceptions in Spring's DataAccessException hierarchy. This bean post processor will only translate exceptions for beans annotated with @Repository.

<beans ...>
    ...
    <bean class="org.springframework.dao.annotation.
How It Works
PersistenceExceptionTranslationPostProcessor" /> </beans>

In Spring, @Repository is a stereotype annotation. By annotating it, a component class can be auto-detected through component scanning. You can assign a component name in this annotation and have the session factory auto-wired by the Spring IoC container with @Autowired.

package com.apress.springrecipes.course.hibernate;
...
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

@Repository("courseDao")
public class HibernateCourseDao implements CourseDao {

    private SessionFactory sessionFactory;
@Autowired
    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }
    ...
}

Then, you can simply enable the <context:component-scan> element and delete the original HibernateCourseDao bean declaration.

<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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

    <context:component-scan
        base-package="com.apress.springrecipes.course.hibernate" />
    ...
</beans>

Persisting Objects with JPA's Context Injection

Problem

In a Java EE environment, a Java EE container can manage entity managers for you and inject them into your EJB components directly. An EJB component can simply perform persistence operations on an injected entity manager without caring much about the entity manager creation and transaction management.

Similarly, Spring provides JpaTemplate to simplify your DAO implementation by managing entity managers and transactions for you. However, using Spring's JpaTemplate means your DAO is dependent on Spring's API.

Solution

An alternative to Spring's JpaTemplate is to use JPA's context injection. Originally, the @PersistenceContext annotation is used for entity manager injection in EJB components. Spring can also interpret this annotation by means of a bean post processor. It will inject an entity manager into a property with this annotation. Spring ensures that all your persistence operations within a single transaction will be handled by the same entity manager.

How It Works

To use the context injection approach, you can declare an entity manager field in your DAO and annotate it with the @PersistenceContext annotation. Spring will inject an entity manager into this field for you to persist your objects.

package com.apress.springrecipes.course.jpa;
...
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.springframework.transaction.annotation.Transactional;

public class JpaCourseDao implements CourseDao {

    @PersistenceContext
    private EntityManager entityManager;

    @Transactional
    public void store(Course course) {
        entityManager.merge(course);
    }

    @Transactional
    public void delete(Long courseId) {
        Course course = entityManager.find(Course.class, courseId);
        entityManager.remove(course);
    }

    @Transactional(readOnly = true)
    public Course findById(Long courseId) {
        return entityManager.find(Course.class, courseId);
    }

    @Transactional(readOnly = true)
    public List<Course> findAll() {
        Query query = entityManager.createQuery("from Course");
        return query.getResultList();
    }
}

You can annotate each DAO method or the entire DAO class with @Transactional to make all these methods transactional. It ensures that the persistence operations within a DAO method will by executed in the same transaction and hence by the same entity manager.

In the bean configuration file for JPA (i.e., beans-jpa.xml), you have to declare a JpaTransactionManager instance and enable declarative transaction management via <tx:annotation-driven>. You have to register a PersistenceAnnotationBeanPostProcessor instance to inject entity managers into properties annotated with @PersistenceContext.

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="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">
    ...
    <tx:annotation-driven />

    <bean id="transactionManager"
        class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>

    <bean name="courseDao"
        class="com.apress.springrecipes.course.jpa.JpaCourseDao" />

    <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
</beans>

A PersistenceAnnotationBeanPostProcessor instance will be registered automatically once you enable the <context:annotation-config> element. So, you can delete its explicit bean declaration.

<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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

    <context:annotation-config />
    ...
</beans>

This bean post processor can also inject the entity manager factory into a property with the @PersistenceUnit annotation. This allows you to create entity managers and manage transactions by yourself. It's no different from injecting the entity manager factory via a setter method.

package com.apress.springrecipes.course.jpa;
...
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceUnit;
public class JpaCourseDao implements CourseDao {
   @PersistenceContext
    private EntityManager entityManager;

    @PersistenceUnit
    private EntityManagerFactory entityManagerFactory;
    ...
}

Remember that JpaTemplate will translate the native JPA exceptions into exceptions in Spring's DataAccessException hierarchy. However, when calling native methods on a JPA entity manager, the exceptions thrown will be of native type PersistenceException, or other Java SE exceptions like IllegalArgumentException and IllegalStateException. If you want JPA exceptions to be translated into Spring's DataAccessException, you have to apply the @Repository annotation to your DAO class.

package com.apress.springrecipes.course.jpa;
...
import org.springframework.stereotype.Repository;

@Repository("courseDao")
public class JpaCourseDao implements CourseDao {
    ...
}

Then, register a PersistenceExceptionTranslationPostProcessor instance to translate the native JPA exceptions into exceptions in Spring's DataAccessException hierarchy. You can also enable <context:component-scan> and delete the original JpaCourseDao bean declaration because @Repository is a stereotype annotation in Spring 2.5 and beyond.

<beans ...>
    ...
    <context:component-scan
        base-package="com.apress.springrecipes.course.jpa" />

    <bean class="org.springframework.dao.annotation.PersistenceException
TranslationPostProcessor" />
</beans>

Summary

This chapter discussed how to use Spring's support for JDBC, Hibernate, and JPA. You learned how to configure a DataSource to connect to a database and how to use Spring's JdbcTemplate, HibernateTemplate, and JpaTemplate to rid your code of tedious boilerplate handling. You saw how to use the utility base classes to build DAO classes with JDBC, Hibernate, and JPA, as well as how to use Spring's support for stereotype annotations and component scanning to easily build new DAOs and Services with a minimum of XML.

In the next chapter, you will learn how to use transactions (i.e., for JMS or a database) with Spring to help ensure consistent state in your services.

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

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