Data persistence and retrieval are inevitable operations in an enterprise world. The advent of JDBC paved the way to interact with multiple databases with ease and comfort. JDBC techlogies have gained popularity in no time because of their unified API to access any database, be it MySQL or Oracle or Sybase or any Relational Database System.
Spring took one step ahead and created an even lighter framework, abstracting the JDBC behind the scenes. Although the JDBC and Spring marriage makes a happy family, there are some unsophisticated or unavailable features from the joint venture. One feature that comes to mind is the support for Object Relational mappings. We still have to write plain old SQL statements to access the data from persistent stores. This is the opportunity Hibernate grabbed and became an instant hit! With millions of downloads over the time, it is now a popular and powerful open source framework. Spring added more abstraction on top of the already powerful Hibernate to make it even better.
This chapter explains how the Spring Framework can be used effectively for accessing databases, without even having to worry about connections and statements. We then continue on to Spring’s ORM support, using Hibernate.
The joint venture did not attempt to bridge the gap between Objects and Relational Data. JDBC is certainly one of the first-hand choices for a Java developer when working with databases. JDBC abstracts away the intricacies involved in accessing different databases. It gives a clear and concise API to do the job easily.
As many developers who worked with JDBC will moan about, there is a lot of redundant or boilerplate code that needs to be written (or cut and pasted!), even if our intention is to fetch a single row of data.
The Spring Framework has changed this scenario drastically. Using a simple template pattern, Spring has revolutionized database access, digesting the boilerplate code altogether into itself. We do not have to worry about the unnecessary bootstrapping and resource management code or ugly exceptions. We are at last able to do what we have been employed to do—write just the business logic. We will see in this chapter how the framework has achieved this objective.
There is a second scenario to consider: we often wish to work with relational entities as if they are objects in your code. As relational entities differ from Java Objects, unless there is a bridge between them, this wish will not be fulfilled.
The good news that there are softwares built to manage this gap—simply called as Object Relational Mapping (ORM) frameworks. Hibernate, Java Data Objects (JDO), iBatis, and TopLink belong to this category. Using these ORM tools, we do not have to work at a low level as exposed by JDBC; instead we manipulate the data as objects.
For example, a table called MOVIES
consists of many rows (Movies). The
analogue to this relational entity would be modeled as a Movie
object in our code, and the mapping of the
MOVIE
row to Movie
domain object is performed by the
framework behind the scenes.
We can agree that JDBC is a simple API for data access. However,
when it comes to coding, it is still cumbersome, as we still have to
write unnecessary, rudimentary code. Some say that about 80 percent of
the code is repetitious. In a world of reusabiltiy, this is
unacceptable. Spring does exactly this—abstracts away all the resource
management so we can concentrate on the meat of the application. It
might not surprise you to know that Spring “reuses” its template design
pattern, allowing us to interact with the databases in a clean and easy
manner. The core of the JDBC package revolves around one class: JdbcTemplate
. This class plays the key role in
accessing data from our components.
The basic and most useful class from the framework is the
JdbcTemplate
. This call should
serve to do most of your work. But should you require a bit more
sophistication, the two variants of JdbcTemplate
—the SimpleJdbcTemplate
and NamedParameterJdbcTemplate
—should provide
you that.
The JdbcTemplate
class provides the common aspects
of database operations, such as inserting and updating data, using prepared statements,
querying tables by using standard SQL queries, invoking stored procedures, and so on. It
can also iterate over the ResultSet
data efficiently
and effectively.
The connection management is hidden from the user, and so is the
resource pooling and exception management. Regarding the exceptions,
one does not have to clutter the code with try-catch
blocks because the
database-specific exceptions are wrapped under covers by Spring’s
Runtime Exceptions.
As always inline with the template design pattern, the JdbTemplate
provides callback interfaces for
us to implement our business logic. For example, PreparedStatementCallback
is used for
creating PreparedStatement
s, while
RowCallbackHandler
is where you
extract the ResultSet
into your
domain objects. The CallableStatementCallback
is used when
executing a stored procedure. We will work briefly with these
callbacks in the next few sections.
Before we can jump into working with the JdbcTemplate
, we need to take care of a few details about its creation.
First, we need to supply a DataSource
to the JdbcTemplate
so it can configure itself to get database
access. As we have seen in the previous chapter (JMS), just as
ConnectionFactory
was the gateway to a JMS Provider, so is the
DataSource
the gateway to our Database. It supplies the connection
information which is used by the JdbcTemplate to get access to the underlying
databse.
You can configure the DataSource
in the XML file as
shown in the following code snippet. I am using an open source JDBC
framework—Apache commons DBCP—for creating my datasources.
<bean
id=
"movieDataSource"
class=
"org.apache.commons.dbcp.BasicDataSource"
destroy-method=
"close"
>
<property
name=
"driverClassName"
value=
"${jdbc.driver}"
/>
<property
name=
"url"
value=
"${jdbc.url}"
/>
<property
name=
"username"
value=
"${jdbc.username}"
/>
<property
name=
"password"
value=
"${jdbc.password}"
/>
</bean>
Once we have the datasource created, our next job is to create
the JdbcTemplate
.
We have primarily two options: First, we can instantiate a JdbcTemplate
in our Data Access Object (DAO), injecting the DataSource
into it. Alternatively, we can define the JdbcTemplate
in the XML file, wiring the datasource to it, and
then injecting the JdbcTemplate
reference into the DAO
class. The following snippet uses this second option:
<bean
id=
"jdbcTemplate"
class=
"org.springframework.jdbc.core.JdbcTemplate"
>
<property
name=
"dataSource"
ref=
"movieDataSource"
/>
</bean>
<bean
id=
"movieDataSource"
class=
"org.apache.commons.dbcp.BasicDataSource"
destroy-method=
"close"
>
....</bean>
The JdbcTemplate
is a
threadsafe object once configured, so you can inject it into any
number of DAOs.
Let’s define the DAO for accessing the MOVIES
database. The following snippet shows this class:
public
class
MovieDAO
implements
IMovieDAO
{
private
JdbcTemplate
jdbcTemplate
=
null
;
private
void
setJdbcTemplate
(
JdbcTemplate
jdbcTemplate
){
this
.
jdbcTemplate
=
jdbcTemplate
;
}
private
JdbcTemplate
getJdbcTemplate
()
{
return
this
.
jdbcTemplate
;
}
...
}
The IMovieDAO is the interface that has all the movie-related functionality which is shown here:
public
interface
IMovieDAO
{
public
Movie
getMovie
(
String
id
);
public
String
getStars
(
String
title
);
public
List
<
Movie
>
getMovies
(
String
sql
);
public
List
<
Movie
>
getAllMovies
();
public
void
insertMovie
(
Movie
m
);
public
void
updateMovie
(
Movie
m
);
public
void
deleteMovie
(
String
id
);
public
void
deleteAllMovies
();
}
The MovieDAO
has JdbcTemplate
as a member variable. It is configured and wired with a
datasource in the XML file and injected into our concrete DAO. The wiring XML file is
shown here:
<bean
id=
"movieDao"
class=
"com.madhusudhan.jscore.data.MovieDAO"
>
<property
name=
"jdbcTemplate"
ref=
"jdbcTemplate"
/>
</bean>
<bean
id=
"jdbcTemplate"
class=
"org.springframework.jdbc.core.JdbcTemplate"
>
<property
name=
"dataSource"
ref=
"movieDataSource"
/>
</bean>
<bean
id=
"movieDataSource"
class=
"org.apache.commons.dbcp.BasicDataSource"
destroy-method=
"close"
>
....</bean>
Here
is a snippet of the MovieDAO
class.
public
class
MovieDAO
implements
IMovieDAO
{
private
JdbcTemplate
jdbcTemplate
=
null
;
private
void
setJdbcTemplate
(
JdbcTemplate
jdbcTemplate
){
this
.
jdbcTemplate
=
jdbcTemplate
;
}
private
JdbcTemplate
getJdbcTemplate
()
{
return
this
.
jdbcTemplate
;
}
...
}
That’s
it! Your JdbcTemplate
is configured and ready to be
used straightaway. Let’s concentrate on what we can do with the template in our
hand.
The simplest operation is to fetch movie actors using a criteria such as a movie
title. We can write a simple SQL query like select stars from
MOVIES
to retrieve the movie actors.
Our MovieDAO
has a method getStars
()
that takes a title and returns
comma-separated stars list. The snippet here shows the implementation of this
query:
@Override
public
String
getStars
(
String
title
)
{
String
stars
=
getJdbcTemplate
().
queryForObject
(
"select stars from MOVIES where title='"
+
title
+
"'"
,
String
.
class
);
return
stars
;
}
The
query was executed using queryForObject
method on
JdbcTemplate
. This method takes two parameters, a SQL
query without bind variables and an expected type of the result.
The expected result is a comma-separated stars list. We can improve this query by
parameterizing the query. The where
clause will have a
bind variable that will change on queries. It is shown in the listing here:
@Override
public
String
getStars
(
String
title
)
{
// using where clause
String
stars
=
getJdbcTemplate
().
queryForObject
(
"select stars from MOVIES where title=?"
,
new
Object
[]{
title
},
String
.
class
);
return
stars
;
}
Here, ideally the second argument is passed via method arguments.
There are plenty of queryForXXX
methods defined on
the JdbcTemplate
, such as queryForInt
, queryForList
, queryForMap
, and so on. Please refer to the Spring Framework’s
API to understand the workings of these various methods.
The previous queries returned a single piece of data, movie stars, as we have seen in that case.
How can we retrieve a single row, say a Movie
object for a given an id? The JdbcTemplate
’s queryForObject
method comes handy for such
requirement. One additional thing we need to do is to pass a RowMapper
instance.
The JDBC API returns a ResultSet
, and we need to
map each and every column of data from the ResultSet
onto our domain objects. The Spring framework eliminates this repetitious process by
providing the RowMapper
interface.
Simply put, RowMapper
is an
interface for mapping table rows to a domain object. It has one method
called mapRow
that should be
implemented by the concrete classes.
What we need to do is implement this interface to map our table columns to a Movie
object. Let’s implement a row mapper for our Movie
domain object—see the snippet
here:
public
class
MovieRowMapper
implements
RowMapper
{
Movie
movie
=
null
;
public
Object
mapRow
(
ResultSet
rs
,
int
rowNum
)
throws
SQLException
{
movie
=
new
Movie
();
// This is where we extract data
// from ResultSet and set it on Movie object
movie
.
setID
(
rs
.
getString
(
"id"
));
movie
.
setTitle
(
rs
.
getString
(
"title"
));
...
return
movie
;
}
}
Basically, the idea is to extract the relevant columns from the
ResultSet
and populate our Movie
domain object and return it.
Now that our MovieRowMapper
is implemented, use
jdbcTemplate
to retrieve the results.
@Override
public
Movie
getMovie
(
String
id
){
String
sql
=
"select * from MOVIES where id=?"
;
return
getJdbcTemplate
().
queryForObject
(
sql
,
new
Object
[]{
id
},
new
MovieRowMapper
());
}
The JdbcTemplate
executes the
query by binding the argument and invoking the MovieRowMapper
with a returned ResultSet
from the query.
We need to enhance this logic a bit more to get all the movies (as a list) instead of a single movie result.
We can use the same MovieRowMapper
for returning
all the movies. However, it should be wrapped in a RowMapperResultSetExtractor
as shown here:
public
List
getAllMovies
(){
RowMapper
mapper
=
new
MovieRowMapper
();
String
sql
=
"select * from MOVIES"
;
return
getJdbcTemplate
().
query
(
sql
,
new
RowMapperResultSetExtractor
(
mapper
,
10
));
}
We can use the jdbcTemplate.update()
method to
insert, update, or delete the data. The following code shows the insertion of Movie
into our database.
@Override
public
void
insertMovie
(
Movie
m
)
{
String
sql
=
"insert into MOVIES (ID, TITLE, GENRE, SYNOPSIS)
values(?,?,?,?)"
;
// The insert parameters
Object
[]
params
=
new
Object
[]
{
m
.
getID
(),
m
.
getTitle
(),
m
.
getGenre
(),
m
.
getSynopsis
()
};
// The insert parameters types
int
[]
types
=
new
int
[]
{
Types
.
VARCHAR
,
Types
.
VARCHAR
,
Types
.
VARCHAR
,
Types
.
VARCHAR
};
// Run the query
jdbcTemplate
.
update
(
sql
,
params
,
types
);
}
The second and third parameters indicate the input values and their respective types.
Similarly, deleting a single movie from the database is straightforward:
@Override
public
void
deleteMovie
(
String
id
)
{
String
sql
=
"delete from MOVIES where ID=?"
;
Object
[]
params
=
new
Object
[]
{
id
};
jdbcTemplate
.
update
(
sql
,
params
);
}
In order to delete all movies, use the following code:
@Override
public
void
deleteAllMovies
(){
String
sql
=
"delete from MOVIES"
;
jdbcTemplate
.
update
(
sql
);
}
Calling stored procedures is also an easy thing, using the update method:
public
void
deleteAllMovies
(){
String
sql
=
"call MOVIES.DELETE_ALL_MOVIES"
;
jdbcTemplate
.
update
(
sql
);
}
As we have noticed, the JdbcTemplate
has eased our
burden in accessing the database dramatically. I advise you to refer to Spring’s API for
more such methods on the template class.
Hibernate provides a mapping of database columns to the objects by reading some configurations. We define the mapping of our domain objects to the table columns in the XML configuration file.
The configuration file for each of the mappings should have an extension of
.hbm.xml. Spring abstracts the framework one step more and provides
us with classes like HibernateTemplate
to access the
database. However, Spring advocates to drop HibernateTemplate
in favour
of using Hibernate’s native API.
We’ll look into details in a minute, but first let’s see how we can prepare the required configurations.
For example, let’s define our MOVIE
object by using
hibernate mapping rules.
<hibernate-mapping>
<class
name=
"com.madhusudhan.jscore.data.Movie"
table=
"MOVIES"
>
<id
name=
"id"
column=
"ID"
>
<generator
class=
"assigned"
/>
</id>
<property
name=
"title"
column=
"TITLE"
/>
<property
name=
"genre"
column=
"GENRE"
/>
<property
name=
"synopsis"
column=
"SYNOPSIS"
/>
</class>
</hibernate-mapping>
The class
attribute defines the
actual domain class, Movie
in this
case. The id
attribute is the primary
key and is set as assigned
, meaning it is the
application’s responsibility to set the primary key. The rest of the
properties are mapped against the respective columns on the MOVIES
table.
Hibernate requires a Session
object in order to access the database. A Session
is created from the SessionFactory
. When using Spring framework,
you can use Spring’s LocalSessionFactoryBea
n
to create this SessionFactory
. The LocalSessionFactoryBean
requires a datasource
to be wired in, along with hibernate properties and mapping
resources.
The hibernateProperties
attribute on the factory bean enables the
database specific properties such as database dialect, pool sizes, and other options. The
mappingResources
property loads the mapping config files
(Movie.hbm.xml, in our case).
<bean
id=
"sessionFactory"
class=
"org.springframework.orm.hibernate.LocalSessionFactoryBean"
>
<property
name=
"dataSource"
ref=
"movieDataSource"
/>
<property
name=
"hibernateProperties"
>
<props>
<prop
key=
"hibernate.dialect"
>
net.sf.hibernate.dialect.MySQLDialect</prop>
<prop
key=
"hibernate.show_sql"
>
false</prop>
</props>
</property>
<property
name=
"mappingResources"
>
<list>
<value>
Movie.hbm.xml</value>
</list>
</property>
</bean>
Now that the sessionFactory
is defined, the next bit is to define HibernateTemplate
. The HibernateTemplate
requires a SessionFactory
instance, so the following
declaration wires the sessionFactory
that we defined above.
<bean
id=
"hibernateTemplate"
class=
"org.springframework.orm.hibernate.HibernateTemplate"
>
<property
name=
"sessionFactory"
ref="sessionFactory"/></bean>
<bean
id=
"movieDao"
class=
"com.madhusudhan.jscore.data.MovieDAO"
>
<property
name=
"hibernateTemplate"
ref="hibernateTemplate"/></bean>
That’s all—the configuration is completed. Let’s see how we get the data from our database.
The getMovie
method shown here uses the template’s
load method.
public
Movie
getMovie
(
String
id
){
//Searching for a movie
return
(
Movie
)
getHibernateTemplate
().
load
(
Movie
.
class
,
id
);
}
As you can see, there’s no SQL that retrieves a movie in this method. It feels like we are working with Java objects rather than data! The load method accesses the database to load the matching row based on the id passed.
Updating a Movie is simple as well:
public
void
updateMovie
(
Movie
m
){
//Updating a movie
getHibernateTemplate
().
update
(
m
);
}
As you can see, the single statement above does the job! Deleting a row is as simple as invoking the delete method.
public
void
deleteMovie
(
Movie
m
){
// Deleting a movie
getHibernateTemplate
().
delete
(
m
);
}
Running queries is straightforward, too. Hibernate introduces
Hibernate Query Language (HQL) for writing queries. Use
find
methods to execute such queries.
For example, returning a Movie based on a ID is shown here:
public
Movie
getMovie
(
String
id
){
String
sql
=
"from MOVIES as movies where movies.id=?"
;
// Finding a movie
return
(
Movie
)
getHibernateTemplate
().
find
(
sql
,
id
);
}
That’s about using Hibernate from a very high ground. I strongly advise you to pick up any Hibernate book to understand in detail.
The recomended way to use Spring with Hibernate is to use Hibernate’s API rather than Spring’s template wrapper. The subject is provided at length in my other book Just Spring Data Access, so please go through it if you have the copy. For completness, let’s browse through this style quickly.
The Hibernates’s org.hibernate.SessionFactory
and org.hibernate.Session
forms the central part of
the Hibernate API.
Spring provides a
org.springframework.orm.hibernate3.LocalSessionFactoryBean
to create a wrapper around the SessionFactory
. This
wrapper is then injected into our DAO object.
As expected, this bean should be wired in with a
DataSource
, along with other Hibernate properties.
The configuration is almost same as the one shown earlier:
<bean
id=
"sessionFactory"
class=
"org.springframework.orm.hibernate3.LocalSessionFactoryBean"
>
<property
name=
"dataSource"
ref=
"mySqlDataSource"
/>
<property
name=
"mappingResources"
>
<list>
<value>
Trade.hbm.xml</value>
</list>
</property>
<property
name=
"hibernateProperties"
>
<props>
<prop
key=
"hibernate.show_sql"
>
false</prop>
<prop
key=
"hibernate.hbm2ddl.auto"
>
true</prop>
....</props>
</bean>
The TradeDAO
, along with the injected
SessionFactory, is declared here:
public
class
TradeDAO
{
private
SessionFactory
sessionFactory
=
null
;
public
SessionFactory
getSessionFactory
()
{
return
sessionFactory
;
}
public
void
setSessionFactory
(
SessionFactory
sessionFactory
)
{
this
.
sessionFactory
=
sessionFactory
;
}
}
Once we have the handle to the SessionFactory
, we can fetch a
Session
out of it by using
getSessionFactory().getCurrentSession()
method invocation. The
session is the main interface that we should be using to do the database operations. For
example, in order to persist the Trade, all we need to do is to call a
save
method on this session
. This is shown here:
public
void
persist
(
Trade
t
)
{
session
.
beginTransaction
();
session
.
save
(
t
);
session
.
getTransaction
().
commit
();
}
That’s it, you are done with persisting the trade—no messy SQL or
ResultSets
or RowMapper
s!
Note that each of these database operation should be carried in a transaction, hence we are surrounding our code by beginning a transaction and commiting the same.
There are a plethora of operations that you could carry out on the Session object—refer to the API to get more insight.
In this chapter, we discussed Spring’s support of JDBC and Hibernate. As we have seen in the examples, Spring has truly simplified our lives by providing a simple yet powerful API to work with. We can concentrate on the business logic rather than writing reams of repetitive code fragments.
18.226.164.75