Data access using JDBCIntroductionThe value-add provided by the Spring Framework's JDBC abstraction
framework is perhaps best shown by the following list (note that only the
italicized lines need to be coded by an application developer):Define connection parametersOpen the connectionSpecify the statementPrepare and execute the statementSet up the loop to iterate through the results (if any)Do the work for each iterationProcess any exceptionHandle transactionsClose the connectionThe Spring Framework takes care of all the grungy, low-level details
that can make JDBC such a tedious API to develop with.Choosing a styleThere are a number of options for selecting an approach to form
the basis for your JDBC database access. There are three flavors of the
JdbcTemplate, a new "SimpleJdbc" approach taking advantage of database
metadata, and there is also the "RDBMS Object" style for a more object
oriented approach similar in style to the JDO Query design. We'll
briefly list the primary reasons why you would pick one of these
approaches. Keep in mind that even if you start using one of these
approaches, you can still mix and match if there is a feature in a
different approach that you would like to take advantage of. All
approaches requires a JDBC 2.0 compliant driver and some advanced
features require a JDBC 3.0 driver.JdbcTemplate - this is the
classic Spring JDBC approach and the most widely used. This is the
"lowest level" approach and all other approaches use a JdbcTemplate
under the covers. In Spring 3.0 it has been updated with Java 5
support like generics and varargs.NamedParameterJdbcTemplate -
wraps a JdbcTemplate to provide more convenient usage with named
parameters instead of the traditional JDBC "?" place holders. This
provides better documentation and ease of use when you have multiple
parameters for an SQL statement. It has also been updated with Java
5 support like generics and varargs for Spring 3.0.SimpleJdbcTemplate - this
class combines the most frequently used operations across
JdbcTemplate and NamedParameterJdbcTemplate. It also adds some
additional convenience around support for Java 5 varargs where this
was not possible in the JdbcTemplate due to backwards compatibility
reasons.SimpleJdbcInsert and
SimpleJdbcCall - designed to take advantage of database
metadata to limit the amount of configuration needed. This will
simplify the coding to a point where you only need to provide the
name of the table or procedure and provide a Map of parameters
matching the column names. Designed to work together with the
SimpleJdbcTemplate. Requires a database that provides adequate
metadata.RDBMS Objects including MappingSqlQuery,
SqlUpdate and StoredProcedure - an approach where you
create reusable and thread safe objects during initialization of
your data access layer. This approach is modeled after JDO Query
where you define your query string, declare parameters and compile
the query. Once that is done any execute methods can be called
multiple times with various parameter values passed in. It has also
been updated with Java 5 support like generics and vararg support
for Spring 3.0.The package hierarchyThe Spring Framework's JDBC abstraction framework consists of four
different packages, namely core,
datasource, object, and
support.The org.springframework.jdbc.core package
contains the JdbcTemplate class and its various
callback interfaces, plus a variety of related classes. A sub-package
named org.springframework.jdbc.core.simple contains
the SimpleJdbcTemplate class and the related
SimpleJdbcInsert and
SimpleJdbcCall classes. Another sub-package named
org.springframework.jdbc.core.namedparam contains the
NamedParameterJdbcTemplate class and the related
support classes.The org.springframework.jdbc.datasource package
contains a utility class for easy
DataSource access, and various simple
DataSource implementations that can be
used for testing and running unmodified JDBC code outside of a Java EE
container. A sub-package named
org.springfamework.jdbc.datasource.embedded provides
support for creating in-memory database instances using Java database
engines such as HSQL and H2.Next, the org.springframework.jdbc.object
package contains classes that represent RDBMS queries, updates, and
stored procedures as thread safe, reusable objects. This approach is
modeled by JDO, although of course objects returned by queries are
disconnected from the database. This higher level of JDBC
abstraction depends on the lower-level abstraction in the
org.springframework.jdbc.core package.Finally the org.springframework.jdbc.support
package is where you find the SQLException
translation functionality and some utility classes.Exceptions thrown during JDBC processing are translated to
exceptions defined in the org.springframework.dao
package. This means that code using the Spring JDBC abstraction layer
does not need to implement JDBC or RDBMS-specific error handling. All
translated exceptions are unchecked giving you the option of catching
the exceptions that you can recover from while allowing other exceptions
to be propagated to the caller.Using the JDBC Core classes to control basic JDBC processing and
error handlingJdbcTemplateThe JdbcTemplate class is the central class
in the JDBC core package. It simplifies the use of JDBC since it handles
the creation and release of resources. This helps to avoid common errors
such as forgetting to always close the connection. It executes the core
JDBC workflow like statement creation and execution, leaving application
code to provide SQL and extract results. This class executes SQL
queries, update statements or stored procedure calls, imitating
iteration over ResultSets and extraction
of returned parameter values. It also catches JDBC exceptions and
translates them to the generic, more informative, exception hierarchy
defined in the org.springframework.dao
package.Code using the JdbcTemplate only need to
implement callback interfaces, giving them a clearly defined contract.
The PreparedStatementCreator callback
interface creates a prepared statement given a
Connection provided by this class,
providing SQL and any necessary parameters. The same is true for the
CallableStatementCreator interface which
creates callable statement. The
RowCallbackHandler interface extracts
values from each row of a
ResultSet.The JdbcTemplate can be used within a DAO
implementation via direct instantiation with a
DataSource reference, or be configured in
a Spring IOC container and given to DAOs as a bean reference. Note: the
DataSource should always be configured as
a bean in the Spring IoC container, in the first case given to the
service directly, in the second case to the prepared template.Finally, all of the SQL issued by this class is logged at the
'DEBUG' level under the category corresponding to the
fully qualified class name of the template instance (typically
JdbcTemplate, but it may be different if a custom
subclass of the JdbcTemplate class is being
used).ExamplesFind below some examples of using the
JdbcTemplate class. (These examples are not an
exhaustive list of all of the functionality exposed by the
JdbcTemplate; see the attendant Javadocs for
that).Querying (SELECT)A simple query for getting the number of rows in a
relation.A simple query using a bind variable.Querying for a String.Querying and populating a single domain
object.() {
public Actor mapRow(ResultSet rs, int rowNum) throws SQLException {
Actor actor = new Actor();
actor.setFirstName(rs.getString("first_name"));
actor.setLastName(rs.getString("last_name"));
return actor;
}
});
]]>Querying and populating a number of domain objects. actors = this.jdbcTemplate.query(
"select first_name, last_name from t_actor",
new RowMapper() {
public Actor mapRow(ResultSet rs, int rowNum) throws SQLException {
Actor actor = new Actor();
actor.setFirstName(rs.getString("first_name"));
actor.setLastName(rs.getString("last_name"));
return actor;
}
});
]]>If the last two snippets of code actually existed in the same
application, it would make sense to remove the duplication present
in the two RowMapper anonymous inner
classes, and extract them out into a single class (typically a
static inner class) that can then be referenced
by DAO methods as needed. For example, the last code snippet might
be better off written like so: findAllActors() {
return this.jdbcTemplate.query( "select first_name, last_name from t_actor", new ActorMapper());
}
private static final class ActorMapper implements RowMapper {
public Actor mapRow(ResultSet rs, int rowNum) throws SQLException {
Actor actor = new Actor();
actor.setFirstName(rs.getString("first_name"));
actor.setLastName(rs.getString("last_name"));
return actor;
}
}]]>Updating (INSERT/UPDATE/DELETE)Other operationsThe execute(..) method can be used to
execute any arbitrary SQL, and as such is often used for DDL
statements. It is heavily overloaded with variants taking callback
interfaces, binding variable arrays, and suchlike.Invoking a simple stored procedure (more sophisticated stored
procedure support is covered
later).JdbcTemplate idioms (best
practices)Instances of the JdbcTemplate class are
threadsafe once configured. This is important
because it means that you can configure a single instance of a
JdbcTemplate and then safely inject this
shared reference into multiple DAOs (or
repositories). To be clear, the JdbcTemplate is
stateful, in that it maintains a reference to a
DataSource, but this state is
not conversational state.A common idiom when using the
JdbcTemplate class (and the associated SimpleJdbcTemplate
and NamedParameterJdbcTemplate
classes) is to configure a DataSource
in your Spring configuration file, and then dependency inject that
shared DataSource bean into your DAO
classes; the JdbcTemplate is created in the
setter for the DataSource. This leads
to DAOs that look in part like this:public class JdbcCorporateEventDao implements CorporateEventDao {
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
// JDBC-backed implementations of the methods on the CorporateEventDao follow...
}The corresponding configuration might look like this.]]>An alternative to explicit configuration is to use the component
scanning and annotation support for dependency injection. In this case
we would annotate the setter method for the
DataSource with the
@Autowired annotation.public class JdbcCorporateEventDao implements CorporateEventDao {
private JdbcTemplate jdbcTemplate;
@Autowired
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
// JDBC-backed implementations of the methods on the CorporateEventDao follow...
}The corresponding XML configuration file would look like the
following:]]>If you are using Spring's
JdbcDaoSupport class, and your various
JDBC-backed DAO classes extend from it, then you inherit a
setDataSource(..) method for free from said
superclass. It is totally up to you as to whether or not you inherit
from said class, you certainly are not forced to. If you look at the
source for the JdbcDaoSupport class you will
see that there is not a whole lot to it... it is provided as a
convenience only.Regardless of which of the above template initialization styles
you choose to use (or not), there is (almost) certainly no need to
create a brand new instance of a JdbcTemplate
class each and every time you wish to execute some SQL... remember,
once configured, a JdbcTemplate instance is
threadsafe. A reason for wanting multiple
JdbcTemplate instances would be when you have
an application that accesses multiple databases, which requires
multiple DataSources, and subsequently
multiple differently configured
JdbcTemplates.NamedParameterJdbcTemplateThe NamedParameterJdbcTemplate class adds
support for programming JDBC statements using named parameters (as
opposed to programming JDBC statements using only classic placeholder
('?') arguments. The
NamedParameterJdbcTemplate class wraps a
JdbcTemplate, and delegates to the wrapped
JdbcTemplate to do much of its work. This section
will describe only those areas of the
NamedParameterJdbcTemplate class that differ from
the JdbcTemplate itself; namely, programming JDBC
statements using named parameters.// some JDBC-backed DAO class...
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
}
public int countOfActorsByFirstName(String firstName) {
String sql = "select count(*) from T_ACTOR where first_name = :first_name";
SqlParameterSource namedParameters = new MapSqlParameterSource("first_name", firstName);
return namedParameterJdbcTemplate.queryForInt(sql, namedParameters);
}Notice the use of the named parameter notation in the value
assigned to the 'sql' variable, and the corresponding
value that is plugged into the 'namedParameters'
variable (of type MapSqlParameterSource).If you like, you can also pass along named parameters (and their
corresponding values) to a
NamedParameterJdbcTemplate instance using the
(perhaps more familiar) Map-based style.
(The rest of the methods exposed by the
NamedParameterJdbcOperations - and
implemented by the NamedParameterJdbcTemplate
class) follow a similar pattern and will not be covered here.)// some JDBC-backed DAO class...
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
}
public int countOfActorsByFirstName(String firstName) {
String sql = "select count(*) from T_ACTOR where first_name = :first_name";
Map namedParameters = Collections.singletonMap("first_name", firstName);
return this.namedParameterJdbcTemplate.queryForInt(sql, namedParameters);
}Another nice feature related to the
NamedParameterJdbcTemplate (and existing in the
same Java package) is the
SqlParameterSource interface. You have
already seen an example of an implementation of this interface in one of
the preceding code snippets (the
MapSqlParameterSource class). The entire point of
the SqlParameterSource is to serve as a
source of named parameter values to a
NamedParameterJdbcTemplate. The
MapSqlParameterSource class is a very simple
implementation, that is simply an adapter around a
java.util.Map, where the keys are the
parameter names and the values are the parameter values.Another SqlParameterSource
implementation is the
BeanPropertySqlParameterSource class. This class
wraps an arbitrary JavaBean (that is, an instance of a class that
adheres to the JavaBean
conventions), and uses the properties of the wrapped JavaBean as
the source of named parameter values.public class Actor {
private Long id;
private String firstName;
private String lastName;
public String getFirstName() {
return this.firstName;
}
public String getLastName() {
return this.lastName;
}
public Long getId() {
return this.id;
}
// setters omitted...
}// some JDBC-backed DAO class...
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
}
public int countOfActors(Actor exampleActor) {
// notice how the named parameters match the properties of the above 'Actor' class
String sql =
"select count(*) from T_ACTOR where first_name = :firstName and last_name = :lastName";
SqlParameterSource namedParameters = new BeanPropertySqlParameterSource(exampleActor);
return this.namedParameterJdbcTemplate.queryForInt(sql, namedParameters);
}Remember that the
NamedParameterJdbcTemplate class
wraps a classic JdbcTemplate
template; if you need access to the wrapped
JdbcTemplate instance (to access some of the
functionality only present in the JdbcTemplate
class), then you can use the
getJdbcOperations() method to access the
wrapped JdbcTemplatevia the
JdbcOperations
interface.See also the section entitled for some advice on how to best use
the NamedParameterJdbcTemplate class in the
context of an application.SimpleJdbcTemplateThe idea behind the
SimpleJdbcTemplate is to provide a simpler
interface that takes better advantage of Java 5 features. It was
initially the only JdbcOperations
implementation that provided support for Java 5 enhanced syntax with
generics and varargs. Now, in Spring 3.0, the original
JdbcTemplate has been upgraded to Java 5 as
well, but the SimpleJdbcTemplate still has the
advantage of providing a simpler API when you don't need access to all
the methods that the JdbcTemplate offers. Also,
since the SimpleJdbcTemplate was designed for
Java 5 there are more methods that take advantage of varargs due to
different ordering of the parameters.The SimpleJdbcTemplate class is a wrapper
around the classic JdbcTemplate that takes better
advantage of Java 5 language features such as varargs and
autoboxing.The value-add of the SimpleJdbcTemplate
class in the area of syntactic-sugar is best illustrated with a
'before and after' example. The following code
snippet shows first some data access code using the classic
JdbcTemplate, followed immediately thereafter by
a code snippet that does the same job, only this time using the
SimpleJdbcTemplate.// classic JdbcTemplate-style...
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
public Actor findActor(String specialty, int age) {
String sql = "select id, first_name, last_name from T_ACTOR" +
" where specialty = ? and age = ?";
RowMapper<Actor> mapper = new RowMapper<Actor>() {
public Actor mapRow(ResultSet rs, int rowNum) throws SQLException {
Actor actor = new Actor();
actor.setId(rs.getLong("id"));
actor.setFirstName(rs.getString("first_name"));
actor.setLastName(rs.getString("last_name"));
return actor;
}
};
// notice the wrapping up of the argumenta in an array
return (Actor) jdbcTemplate.queryForObject(sql, new Object[] {id}, mapper);
}Here is the same method, only this time using the
SimpleJdbcTemplate.// SimpleJdbcTemplate-style...
private SimpleJdbcTemplate simpleJdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource);
}
public Actor findActor(String specialty, int age) {
String sql = "select id, first_name, last_name from T_ACTOR" +
" where specialty = ? and age = ?";
RowMapper<Actor> mapper = new RowMapper<Actor>() {
public Actor mapRow(ResultSet rs, int rowNum) throws SQLException {
Actor actor = new Actor();
actor.setId(rs.getLong("id"));
actor.setFirstName(rs.getString("first_name"));
actor.setLastName(rs.getString("last_name"));
return actor;
}
};
// notice the use of varargs since the parameter values now come
// after the RowMapper parameter
return this.simpleJdbcTemplate.queryForObject(sql, mapper, specialty, age);
}See also the section entitled for some advice on how to best use
the SimpleJdbcTemplate class in the context of an
application.The SimpleJdbcTemplate class only offers
a subset of the methods exposed on the
JdbcTemplate class. If you need to use a method
from the JdbcTemplate that is not defined on
the SimpleJdbcTemplate, you can always access
the underlying JdbcTemplate by calling the
getJdbcOperations() method on the
SimpleJdbcTemplate, which will then allow you
to invoke the method that you want. The only downside is that the
methods on the JdbcOperations interface
are not generified, so you are back to casting and such again.DataSourceIn order to work with data from a database, one needs to obtain a
connection to the database. The way Spring does this is through a
DataSource. A
DataSource is part of the JDBC
specification and can be seen as a generalized connection factory. It
allows a container or a framework to hide connection pooling and
transaction management issues from the application code. As a developer,
you don not need to know any details about how to connect to the
database, that is the responsibility for the administrator that sets up
the datasource. You will most likely have to fulfill both roles while
you are developing and testing you code though, but you will not
necessarily have to know how the production data source is
configured.When using Spring's JDBC layer, you can either obtain a data
source from JNDI or you can configure your own, using a connection pool
implementation provided by a third party. Popular ones are Apache
Jakarta Commons DBCP and C3P0. There are some implementations provided
in the Spring distribution, but they are only meant to be used for
testing purposes since they don't provide any pooling.We will use the DriverManagerDataSource
implementation for this section but there are several additional
implementations that will be covered later on. The
DriverManagerDataSource works the same way that
you probably are used to work when you obtain a JDBC connection. You
have to specify the fully qualified class name of the JDBC driver that
you are using so that the DriverManager can load
the driver class. Then you have to provide a URL that varies between
JDBC drivers. You have to consult the documentation for your driver for
the correct value to use here. Finally you must provide a username and a
password that will be used to connect to the database. Here is an
example of how to configure a
DriverManagerDataSource in Java code:We also have an example how this would look in an XML
configuration:]]>The DriverManagerDataSource class should
only be used for testing purposes since it does not provide pooling
and will perform poorly when multiple requests for a connection are
made.Just to provide a more complete picture we will also show the
configuration for DBCP and C3P0. We only show the basic connectivity
configuration here. There are more options documented in the product
documentation for the respective connection pooling implementations that
will help control the pooling features.FIrst we have the DBCP configuration:]]>And last we have the C3P0 configuration:]]>SQLExceptionTranslatorSQLExceptionTranslator is an
interface to be implemented by classes that can translate between
SQLExceptions and Spring's own
data-access-strategy-agnostic
org.springframework.dao.DataAccessException.
Implementations can be generic (for example, using SQLState codes for
JDBC) or proprietary (for example, using Oracle error codes) for greater
precision.SQLErrorCodeSQLExceptionTranslator is the
implementation of SQLExceptionTranslator
that is used by default. This implementation uses specific vendor codes.
More precise than SQLState implementation, but vendor
specific. The error code translations are based on codes held in a
JavaBean type class named SQLErrorCodes. This
class is created and populated by an
SQLErrorCodesFactory which as the name suggests
is a factory for creating SQLErrorCodes based on
the contents of a configuration file named 'sql-error-codes.xml'. This file is
populated with vendor codes and based on the DatabaseProductName taken
from the DatabaseMetaData, the codes for
the current database are used.The SQLErrorCodeSQLExceptionTranslator
applies the following matching rules: Try custom translation implemented by any subclass. Note
that this class is concrete and is typically used itself, in which
case this rule does not apply.Apply error code matching. Error codes are obtained from the
SQLErrorCodesFactory by default. This looks
up error codes from the classpath and keys into them from the
database name from the database metadata.Use the fallback translator.
SQLStateSQLExceptionTranslator is the
default fallback translator.SQLErrorCodeSQLExceptionTranslator can be
extended the following way:In this example the specific error code
'-12345' is translated and any other errors are
simply left to be translated by the default translator implementation.
To use this custom translator, it is necessary to pass it to the
JdbcTemplate using the method
setExceptionTranslator and to use this
JdbcTemplate for all of the data access
processing where this translator is needed. Here is an example of how
this custom translator can be used:private JdbcTemplate jdbcTemoplate;
public void setDataSource(DataSource dataSource) {
// create a JdbcTemplate and set data source
this.jdbcTemplate = new JdbcTemplate();
this.jdbcTemplate.setDataSource(dataSource);
// create a custom translator and set the DataSource for the default translation lookup
CustomSQLErrorCodesTranslator tr = new CustomSQLErrorCodesTranslator();
tr.setDataSource(dataSource);
this.jdbcTemplate.setExceptionTranslator(tr);
}
public void updateShippingCharge(long orderId, long pct) {
// use the prepared JdbcTemplate for this update
this.jdbcTemplate.update(
"update orders" +
" set shipping_charge = shipping_charge * ? / 100" +
" where id = ?"
pct, orderId);
}The custom translator is passed a data source because we still
want the default translation to look up the error codes in
sql-error-codes.xml.Executing statementsTo execute an SQL statement, there is very little code needed. All
you need is a DataSource and a
JdbcTemplate. Once you have that, you can use a
number of convenience methods that are provided with the
JdbcTemplate. Here is a short example showing
what you need to include for a minimal but fully functional class that
creates a new table.Running QueriesIn addition to the execute methods, there is a large number of
query methods. Some of these methods are intended to be used for queries
that return a single value. Maybe you want to retrieve a count or a
specific value from one row. If that is the case then you can use
queryForInt(..),
queryForLong(..) or
queryForObject(..). The latter will convert the
returned JDBC Type to the Java class that is
passed in as an argument. If the type conversion is invalid, then an
InvalidDataAccessApiUsageException will
be thrown. Here is an example that contains two query methods, one for
an int and one that queries for a
String.In addition to the single results query methods there are several
methods that return a List with an entry for each row that the query
returned. The most generic method is
queryForList(..) which returns a
List where each entry is a
Map with each entry in the map
representing the column value for that row. If we add a method to the
above example to retrieve a list of all the rows, it would look like
this:> getList() {
return this.jdbcTemplate.queryForList("select * from mytable");
}]]>The list returned would look something like this:Updating the databaseThere are also a number of update methods that you can use. Find
below an example where a column is updated for a certain primary key. In
this example an SQL statement is used that has place holders for row
parameters. Note that the parameter values can be passed in as varargs
or alternatively as an array of objects (and thus primitives should
either be wrapped in the primitive wrapper classes either explicitly or
using auto-boxing).Retrieving auto-generated keysOne of the update convenience methods
provides support for acquiring the primary keys generated by the
database (part of the JDBC 3.0 standard - see chapter 13.6 of the
specification for details). The method takes a
PreparedStatementCreator as its first argument,
and this is the way the required insert statement is specified. The
other argument is a KeyHolder, which will contain
the generated key on successful return from the update. There is not a
standard single way to create an appropriate
PreparedStatement (which explains why the method
signature is the way it is). An example that works on Oracle and may not
work on other platforms is:final String INSERT_SQL = "insert into my_test (name) values(?)";
final String name = "Rob";
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(
new PreparedStatementCreator() {
public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
PreparedStatement ps =
connection.prepareStatement(INSERT_SQL, new String[] {"id"});
ps.setString(1, name);
return ps;
}
},
keyHolder);
// keyHolder.getKey() now contains the generated keyControlling database connectionsDataSourceUtilsThe DataSourceUtils class is a convenient
and powerful helper class that provides static
methods to obtain connections from JNDI and close connections if
necessary. It has support for thread-bound connections, for example for
use with DataSourceTransactionManager.SmartDataSourceThe SmartDataSource interface is to
be implemented by classes that can provide a connection to a relational
database. Extends the DataSource
interface to allow classes using it to query whether or not the
connection should be closed after a given operation. This can sometimes
be useful for efficiency, in the cases where one knows that one wants to
reuse a connection.AbstractDataSourceThis is an abstract base class for Spring's
DataSource implementations, that takes
care of the "uninteresting" glue. This is the class one would extend if
one was writing one's own DataSource
implementation.SingleConnectionDataSourceThe SingleConnectionDataSource class is an
implementation of the SmartDataSource
interface that wraps a singleConnection that is
not closed after use. Obviously, this is not
multi-threading capable.If client code will call close in the assumption of a pooled
connection, like when using persistence tools, set
suppressClose to true. This will
return a close-suppressing proxy instead of the physical connection. Be
aware that you will not be able to cast this to a native Oracle
Connection or the like anymore.This is primarily a test class. For example, it enables easy
testing of code outside an application server, in conjunction with a
simple JNDI environment. In contrast to
DriverManagerDataSource, it reuses the same
connection all the time, avoiding excessive creation of physical
connections.DriverManagerDataSourceThe DriverManagerDataSource class is an
implementation of the standard DataSource
interface that configures a plain old JDBC Driver via bean properties,
and returns a new Connection every
time.This is potentially useful for test or standalone environments
outside of a Java EE container, either as a
DataSource bean in a Spring IoC
container, or in conjunction with a simple JNDI environment.
Pool-assuming Connection.close() calls will simply
close the connection, so any
DataSource-aware persistence code should
work. However, using JavaBean style connection pools such as
commons-dbcp is so easy, even in a test environment, that it is almost
always preferable to use such a connection pool over
DriverManagerDataSource.TransactionAwareDataSourceProxyTransactionAwareDataSourceProxy is a proxy
for a target DataSource, which wraps that
target DataSource to add awareness of
Spring-managed transactions. In this respect it is similar to a
transactional JNDI DataSource as provided
by a Java EE server.It should almost never be necessary or desirable to use this
class, except when existing code exists which must be called and
passed a standard JDBC DataSource
interface implementation. In this case, it's possible to still have
this code be usable, but participating in Spring managed transactions.
It is generally preferable to write your own new code using the higher
level abstractions for resource management, such as
JdbcTemplate or
DataSourceUtils.(See the
TransactionAwareDataSourceProxy Javadocs for more
details.)DataSourceTransactionManagerThe DataSourceTransactionManager class is a
PlatformTransactionManager implementation
for single JDBC datasources. It binds a JDBC connection from the
specified data source to the currently executing thread, potentially
allowing for one thread connection per data source.Application code is required to retrieve the JDBC connection via
DataSourceUtils.getConnection(DataSource) instead of
Java EE's standard DataSource.getConnection. This is
recommended anyway, as it throws unchecked
org.springframework.dao exceptions instead of checked
SQLExceptions. All framework classes like
JdbcTemplate use this strategy implicitly. If not
used with this transaction manager, the lookup strategy behaves exactly
like the common one - it can thus be used in any case.The DataSourceTransactionManager class
supports custom isolation levels, and timeouts that get applied as
appropriate JDBC statement query timeouts. To support the latter,
application code must either use JdbcTemplate or
call DataSourceUtils.applyTransactionTimeout(..)
method for each created statement.This implementation can be used instead of
JtaTransactionManager in the single resource
case, as it does not require the container to support JTA. Switching
between both is just a matter of configuration, if you stick to the
required connection lookup pattern. Note that JTA does not support
custom isolation levels!NativeJdbcExtractorThere are times when we need to access vendor specific JDBC
methods that differ from the standard JDBC API. This can be problematic
if we are running in an application server or with a
DataSource that wraps the
Connection, Statement and
ResultSet objects with its own wrapper objects.
To gain access to the native objects you can configure your
JdbcTemplate or
OracleLobHandler with a
NativeJdbcExtractor.The NativeJdbcExtractor comes in a variety of flavors to match
your execution environment:SimpleNativeJdbcExtractorC3P0NativeJdbcExtractorCommonsDbcpNativeJdbcExtractorJBossNativeJdbcExtractorWebLogicNativeJdbcExtractorWebSphereNativeJdbcExtractorXAPoolNativeJdbcExtractorUsually the SimpleNativeJdbcExtractor is
sufficient for unwrapping a Connection object in
most environments. See the Java Docs for more details.JDBC batch operationsMost JDBC drivers provide improved performance if you batch multiple
calls to the same prepared statement. By grouping updates into batches you
limit the number of round trips to the database. This section will cover
batch processing using both the JdbcTemplate and the
SimpleJdbcTemplate.Batch operations with the JdbcTemplateUsing the JdbcTemplate batch processing is accomplished by
implementing a special interface,
BatchPreparedStatementSetter, and passing that in
as the second parameter in your batchUpdate
method call. This interface has two methods you must implement. One is
named getBatchSize and here you provide the size
of the current batch. The other method is
setValues and it allows you to set the values for
the parameters of the prepared statement and. This method will get
called the number of times that you specified in the
getBatchSize call. Here is an example of this
where we update the actor table based on entries in a list. The entire
list is used as the batch in his example. actors) {
int[] updateCounts = jdbcTemplate.batchUpdate(
"update t_actor set first_name = ?, last_name = ? where id = ?",
new BatchPreparedStatementSetter() {
public void setValues(PreparedStatement ps, int i) throws SQLException {
ps.setString(1, actors.get(i).getFirstName());
ps.setString(2, actors.get(i).getLastName());
ps.setLong(3, actors.get(i).getId().longValue());
}
public int getBatchSize() {
return actors.size();
}
} );
return updateCounts;
}
// ... additional methods
}]]>If you are processing stream of updates or reading from a
file then you might have a preferred batch size, but the last batch
might not have that number of entries. In this case you can use the
InterruptibleBatchPreparedStatementSetter
interface which allows you to interrupt a batch once the input source is
exhausted. The isBatchExhausted method allows you
to signal the end of the batch.Batch operations with the SimpleJdbcTemplateThe SimpleJdbcTemplate provides an
alternate way of providing the batch update. Instead of implementing a
special batch interface, you simply provide all parameter values in the
call and the framework will loop over these values and use an internal
prepared statement setter. The API varies depending on whether you use
named parameters or not. For the named parameters you provide an array
of SqlParameterSource, one entry for each member
of the batch. You can use the
SqlParameterSource.createBatch method to create
this array, passing in either an array of JavaBeans or an array of Maps
containing the parameter values.This example shows a batch update using named parameters: actors) {
SqlParameterSource[] batch = SqlParameterSourceUtils.createBatch(actors.toArray());
int[] updateCounts = simpleJdbcTemplate.batchUpdate(
"update t_actor set first_name = :firstName, last_name = :lastName where id = :id",
batch);
return updateCounts;
}
// ... additional methods
}]]>For an SQL statement using the classic "?" place holders
you pass in a List containing an object array with the update values.
This object array must have one entry for each placeholder in the SQL
statement and they must be in the same order as they are defined in the
SQL statement.The same example using classic JDBC "?" place holders: actors) {
ListAll batch update methods return an int array containing
the number of affected rows for each batch entry. This count is reported
by the JDBC driver and it's not always available in which case the JDBC
driver simply returns a -2 value.Simplifying JDBC operations with the SimpleJdbc classesThe SimpleJdbcInsert and
SimpleJdbcCall classes provide simplified
configuration by taking advantage of database metadata that can be
retrieved via the JDBC driver. This means there is less to configure up
front, although you can override or turn off the metadata processing if
you prefer to provide all the details in your code.Inserting data using SimpleJdbcInsertLet's start by looking at the
SimpleJdbcInsert class first. We will use the
minimal amount of configuration options to start with. The
SimpleJdbcInsert should be instantiated in the
data access layer's initialization method. For this example, the
initializing method is the setDataSource method.
There is no need to subclass the SimpleJdbcInsert
class, just create a new instance and set the table name using the
withTableName method. Configuration methods for
this class follows the "fluid" style returning the instance of the
SimpleJdbcInsert which allows you to chain all
configuration methods. In this case there is only one configuration
method used but we will see examples of multiple ones soon. parameters = new HashMap(3);
parameters.put("id", actor.getId());
parameters.put("first_name", actor.getFirstName());
parameters.put("last_name", actor.getLastName());
insertActor.execute(parameters);
}
// ... additional methods
}]]>The execute method used here takes a plain
java.utils.Map as its only parameter. The
important thing to note here is that the keys used for the Map must
match the column names of the table as defined in the database. This is
because we read the metadata in order to construct the actual insert
statement.Retrieving auto-generated keys using SimpleJdbcInsertNext we'll look at the same insert, but instead of passing in the
id we will retrieve the auto-generated key and set it on the new Actor
object. When we create the SimpleJdbcInsert, in
addition to specifying the table name, we specify the name of the
generated key column using the
usingGeneratedKeyColumns method. parameters = new HashMap(2);
parameters.put("first_name", actor.getFirstName());
parameters.put("last_name", actor.getLastName());
Number newId = insertActor.executeAndReturnKey(parameters);
actor.setId(newId.longValue());
}
// ... additional methods
}]]>Here we can see the main difference when executing the
insert is that we don't add the id to the Map and we call the
executeReturningKey method. This returns a
java.lang.Number object that we can use to create an
instance of the numerical type that is used in our domain class. It's
important to note that we can't rely on all databases to return a
specific Java class here, java.lang.Number is the
base class that we can rely on. If you have multiple auto-generated
columns or the generated values are non-numeric then you can use a
KeyHolder that is returned from the
executeReturningKeyHolder method.Specifying the columns to use for a SimpleJdbcInsertIt's possible to limit the columns used for the insert by
specifying a list of column names to be used. This is accomplished using
the usingColumns method. parameters = new HashMap(2);
parameters.put("first_name", actor.getFirstName());
parameters.put("last_name", actor.getLastName());
Number newId = insertActor.executeAndReturnKey(parameters);
actor.setId(newId.longValue());
}
// ... additional methods
}]]>The execution of the insert is the same as if we had
relied on the metadata for determining what columns to use.Using SqlParameterSource to provide parameter valuesUsing a Map to provide parameter values works fine, but it's not
the most convenient class to use. Spring provides a couple of
implementations of the SqlParameterSource
interface that can be used instead. The first one we'll look at is
BeanPropertySqlParameterSource which is a very
convenient class as long as you have a JavaBean compliant class that
contains your values. It will use the corresponding getter method to
extract the parameter values. Here is an example:Another option is the
MapSqlParameterSource that resembles a Map but
provides a more convenient addValue method that
can be chained.As you can see, the configuration is the same, it;s just
the executing code that has to change to use these alternative input
classes.Calling a stored procedure using SimpleJdbcCallLet's now turn our attention to calling stored procedures using
the SimpleJdbcCall class. This class is designed
to make it as simple as possible to call a stored procedure. It takes
advantage of metadata present in the database to look up names of in and
out parameters. This means that you don't have to explicitly declare
parameters. You can of course still declare them if you prefer to do
that or if you have parameters that don't have an automatic mapping to a
Java class like ARRAY or STRUCT parameters. In our first example we will
look at a plain vanilla procedure that only returns scalar values in
form of VARCHAR and DATE. I have added a birthDate property to the Actor
class to get some variety in terms of return values. The example
procedure reads a specified actor entry and returns first_name,
last_name, and birth_date columns in the form of out parameters. Here is
the source for the procedure as it would look when using MySQL as the
database:As you can see there are four parameters. One is an in
parameter "in_id" containing the id of the Actor we are looking up. The
remaining parameters are out parameters and they will be used to return
the data read from the table.The SimpleJdbcCall is declared in a similar
manner to the SimpleJdbcInsert, no need to
subclass and we declare it in the initialization method. For this
example, all we need to specify is the name of the procedure.The execution of the call involves creating an
SqlParameterSource containing the in parameter.
It's important to match the name of the parameter declared in the stored
procedure. The case doesn't have to match since we use metadata to
determine how database objects should be referred to - what you specify
in your source for the stored procedure is not necessarily the way it is
stored in the database, some databases transform names to all upper case
while others use lower case or the case as specified.The execute method takes the in parameters
and returns a Map containing any out parameters keyed by the name as
specified in the stored procedure. In this case they are
out_first_name, out_last_name and
out_birth_date.The last part of the execute method just
creates an Actor instance to use to return the data retrieved. Again,
it's important to match the names of the out parameters here. Also, the
case used for the names of the out parameters stored in the results map
are as they were defined in the database. You will either have to do a
case-insensitive lookup or instruct Spring to use a
CaseInsensitiveMap from the Jakarta Commons
project. The way you do that is by creating your own
JdbcTemplate and setting the
setResultsMapCaseInsensitive property to
true. Then you pass this customized
JdbcTemplate instance into the constructor of
your SimpleJdbcCall. You also have to include the
commons-collections.jar on your classpath for
this to work. Here is an example of this configuration:By doing this, you don't have to worry about the case
used for the names of your returned out parameters.Declaring parameters to use for a SimpleJdbcCallWe have seen how the parameters are deduced based on metadata, but
you can declare then explicitly if you wish. This is done when the
SimpleJdbcCall is created and configured using
the declareParameters method that takes a
variable number of SqlParameter objects as input.
See the next section for details on how to define an
SqlParameter.We can opt to declare one, some or all of the parameters
explicitly. The parameter metadata is still being used. By calling the
method withoutProcedureColumnMetaDataAccess we
can specify that we would like to bypass any processing of the metadata
lookups for potential parameters and only use the declared ones. Another
situation that can arise is that one or more in parameters have default
values and we would like to leave them out of the call. To do that we
will just call the useInParameterNames to specify
the list of in parameter names to include.This is what a fully declared procedure call declaration of our
earlier example would look like:The execution and end results are the same, we are just
specifying all the details explicitly rather than relying on metadata.
This will be necessary if the database we use is not part of the
supported databases. Currently we support metadata lookup of stored
procedure calls for the following databases: Apache Derby, DB2, MySQL,
Microsoft SQL Server, Oracle and Sybase. We also support metadata lookup
of stored functions for: MySQL, Microsoft SQL Server and Oracle.How to define SqlParametersTo define a parameter to be used for the SimpleJdbc classes, and
also for the RDBMS operations classes covered in the following section,
you use an SqlParameter or one of its subclasses.
You typically specify the parameter name and SQL type in the
constructor. The SQL type is specified using the
java.sql.Types constants. We have already seen
declarations like:The first line with the SqlParameter
declares an in parameter. In parameters can be used for both stored
procedure calls and for queries using the
SqlQuery and its subclasses covered in the
following section.The second line with the SqlOutParameter
declares an out parameter to be used in a stored procedure call. There
is also an SqlInOutParameter for inout
parameters, parameters that provide an in value to the procedure and
that also return a valueOnly parameters declared as SqlParameter
and SqlInOutParameter will be used to provide
input values. This is different from the
StoredProcedure class which for backwards
compatibility reasons allows input values to be provided for
parameters declared as SqlOutParameter.In addition to the name and the SQL type you can specify
additional options. For in parameters you can specify a scale for
numeric data or a type name for custom database types. For out
parameters you can provide a RowMapper to handle
mapping of rows returned from a REF cursor. Another option is to specify
an SqlReturnType that provides and opportunity to
define customized handling of the return values.Calling a stored function using SimpleJdbcCallCalling a stored function is done almost exactly the same way as
calling a stored procedure. The only difference is that you need to
provide a function name rather than a procedure name. This is done by
using the withFunctionName method. Using this
method indicates that your call is to a function and the corresponding
call string for a function call will be generated. There is also a
specialized execute call executeFunction that
will return the function return value as an object of a specified type.
This way you don't have to retrieve the return value from the results
map. A similar convenience method named
executeObject is also available for stored
procedures that only have one out parameter. The following example is
based on a stored function named get_actor_name
that returns an actor's full name. Here is the MySQL source for this
function:To call this function we again create a
SimpleJdbcCall in the initialization
method.The execute method used returns a
String containing the return value from the
function call.Returning ResultSet/REF Cursor from a SimpleJdbcCallCalling a stored procedure or function that returns a result set
has always been a bit tricky. Some databases return result sets during
the JDBC results processing while others require an explicitly
registered out parameter of a specific type. Both approaches still needs
some additional processing to loop over the result set and process the
returned rows. With the SimpleJdbcCall you use
the returningResultSet method and declare a
RowMapper implementation to be used for a
specific parameter. In the case where the result set is returned during
the results processing, there are no names defined, so the returned
results will have to match the order you declare the
RowMapper implementations. The name specified
will still be used to store the processed list of results in the results
map returned from the execute statement.For this example we will use a stored procedure that takes no in
parameters and returns all rows from the t_actor table. Here is the
MySQL source for this procedure:In order to call this procedure we need to declare the
RowMapper to be used. Since the class we want to
map to follows the JavaBean rules, we can use a
ParameterizedBeanPropertyRowMapper that is
created by passing in the required class to map to in the
newInstance method.(0));
return (List) m.get("actors");
}
// ... additional methods
}]]>The execute call passes in an empty Map since this call
doesn't take any parameters. The list of Actors is then retrieved from
the results map and returned to the caller.Modeling JDBC operations as Java objectsThe org.springframework.jdbc.object package
contains classes that allow one to access the database in a more
object-oriented manner. By way of an example, one can execute queries and
get the results back as a list containing business objects with the
relational column data mapped to the properties of the business object.
One can also execute stored procedures and run update, delete and insert
statements.There is a view borne from experience acquired in the field
amongst some of the Spring developers that the various RDBMS operation
classes described below (with the exception of the StoredProcedure
class) can often be replaced with straight
JdbcTemplate calls... often it is simpler to use
and plain easier to read a DAO method that simply calls a method on a
JdbcTemplate direct (as opposed to encapsulating
a query as a full-blown class).It must be stressed however that this is just a
view... if you feel that you are getting measurable
value from using the RDBMS operation classes, feel free to continue
using these classes.SqlQuerySqlQuery is a reusable, threadsafe class
that encapsulates an SQL query. Subclasses must implement the
newRowMapper(..) method to provide a
RowMapper instance that can create one
object per row obtained from iterating over the
ResultSet that is created during the
execution of the query. The SqlQuery class is
rarely used directly since the MappingSqlQuery
subclass provides a much more convenient implementation for mapping rows
to Java classes. Other implementations that extend
SqlQuery are
MappingSqlQueryWithParameters and
UpdatableSqlQuery.MappingSqlQueryMappingSqlQuery is a reusable query in
which concrete subclasses must implement the abstract
mapRow(..) method to convert each row of the
supplied ResultSet into an object of the
type specified. Below is a brief example of a custom query that maps the
data from the t_actor relation to an instance of the
Actor class. {
public ActorMappingQuery(DataSource ds) {
super(ds, "select id, first_name, last_name from t_actor where id = ?");
super.declareParameter(new SqlParameter("id", Types.INTEGER));
compile();
}
@Override
protected Actor mapRow(ResultSet rs, int rowNumber) throws SQLException {
Actor actor = new Actor();
actor.setId(rs.getLong("id"));
actor.setFirstName(rs.getString("first_name"));
actor.setLastName(rs.getString("last_name"));
return actor;
}
}]]>The class extends MappingSqlQuery
parameterized with the Actor type. We provide a
constructor for this customer query that takes the
DataSource as the only parameter. In this
constructor we call the constructor on the superclass with the
DataSource and the SQL that should be
executed to retrieve the rows for this query. This SQL will be used to
create a PreparedStatement so it may
contain place holders for any parameters to be passed in during
execution. Each parameter must be declared using the
declareParameter method passing in an
SqlParameter. The
SqlParameter takes a name and the JDBC type as
defined in java.sql.Types. After all parameters
have been defined we call the compile() method so the
statement can be prepared and later be executed. This class is thread
safe once it has been compiled, so as long as these classes are created
when the DAO is initialized they can be kept as instance variable and be
reused.The method in this example retrieves the customer with the id that
is passed in as the only parameter. Since we only want one object
returned we simply call the convenience method findObject with the id as
parameter. If we instead had a query the returned a list of objects and
took additional parameters then we would use one of the execute methods
that takes an array of parameter values passed in as varargs. searchForActors(int age, String namePattern) {
List actors = actorSearchMappingQuery.execute(age, namePattern);
return actors;
}]]>SqlUpdateThe SqlUpdate class encapsulates an SQL
update. Like a query, an update object is reusable, and like all
RdbmsOperation classes, an update can have
parameters and is defined in SQL. This class provides a number of
update(..) methods analogous to the
execute(..) methods of query objects. This
class is concrete. Although it can be subclassed (for example to add a
custom update method - like in this example where we call it execute) it
can easily be parameterized by setting SQL and declaring
parameters.StoredProcedureThe StoredProcedure class is a superclass
for object abstractions of RDBMS stored procedures. This class is
abstract, and its various
execute(..) methods have protected
access, preventing use other than through a subclass that offers tighter
typing.The inherited sql property will be the name of
the stored procedure in the RDBMS.To define a parameter to be used for the StoredProcedure class,
you use an SqlParameter or one of its subclasses.
You must specify the parameter name and SQL type in the constructor. The
SQL type is specified using the java.sql.Types
constants. We have already seen declarations like:The first line with the SqlParameter
declares an in parameter. In parameters can be used for both stored
procedure calls and for queries using the
SqlQuery and its subclasses covered in the
following section.The second line with the SqlOutParameter
declares an out parameter to be used in the stored procedure call. There
is also an SqlInOutParameter for inout
parameters, parameters that provide an in value to the procedure and
that also return a valueParameters declared as SqlParameter and
SqlInOutParameter will always be used to
provide input values. In addition to this any parameter declared as
SqlOutParameter where an non-null input value
is provided will also be used as an input parameter.In addition to the name and the SQL type you can specify
additional options. For in parameters you can specify a scale for
numeric data or a type name for custom database types. For out
parameters you can provide a RowMapper to handle
mapping of rows returned from a REF cursor. Another option is to specify
an SqlReturnType that provides and opportunity to
define customized handling of the return values.Here is an example of a simple DAO that uses a
StoredProcedure to call a function,
sysdate(), that comes with any Oracle database. To
use the stored procedure functionality you have to create a class that
extends StoredProcedure. In this example the
StoredProcedure class is an inner class, but if
you need to reuse the StoredProcedure you would
declare it as a top-level class. There are no input parameters in this
example, but there is an output parameter that is declared as a date
type using the class SqlOutParameter. The
execute() method executes the procedure and extracts
the returned date from the results Map. The
results Map has an entry for each declared output
parameter, in this case only one, using the parameter name as the
key. results = execute(new HashMap());
Date sysdate = (Date) results.get("date");
return sysdate;
}
}
}]]>Below is an example of a StoredProcedure
that has two output parameters (in this case Oracle REF cursors). execute() {
// again, this sproc has no input parameters, so an empty Map is supplied
return super.execute(new HashMap());
}
}]]>Notice how the overloaded variants of the
declareParameter(..) method that have been used in
the TitlesAndGenresStoredProcedure constructor
are passed RowMapper implementation
instances; this is a very convenient and powerful way to reuse existing
functionality. (The code for the two
RowMapper implementations is provided
below in the interest of completeness.)First the TitleMapper class, which simply
maps a ResultSet to a
Title domain object for each row in the supplied
ResultSet. {
public Title mapRow(ResultSet rs, int rowNum) throws SQLException {
Title title = new Title();
title.setId(rs.getLong("id"));
title.setName(rs.getString("name"));
return title;
}
}]]>Second, the GenreMapper class, which again
simply maps a ResultSet to a
Genre domain object for each row in the supplied
ResultSet. {
public Genre mapRow(ResultSet rs, int rowNum) throws SQLException {
return new Genre(rs.getString("name"));
}
}]]>If you need to pass parameters to a stored procedure (that is the
stored procedure has been declared as having one or more input
parameters in its definition in the RDBMS), you should code a strongly
typed execute(..) method which would delegate to the
superclass' (untyped) execute(Map parameters) (which
has protected access); for example: execute(Date cutoffDate) {
Map inputs = new HashMap();
inputs.put(CUTOFF_DATE_PARAM, cutoffDate);
return super.execute(inputs);
}
}]]>Common issues with parameter and data value handlingThere are some issues involving parameters and data values that are
common across all the different approaches provided by the Spring JDBC
Framework.Providing SQL type information for parametersMost of the time Spring will assume the SQL type of the parameters
based on the type of parameter passed in. It is possible to explicitly
provide the SQL type to be used when setting parameter values. This is
sometimes necessary to correctly set NULL values.There are a few different ways this can be accomplished:Many of the update and query methods of the
JdbcTemplate take an additional parameter in
the form of an int array. This array should contain the SQL type
using constant values from the java.sql.Types
class. There must be one entry for each parameter.You can wrap the parameter value that needs this additional
information using the SqlParameterValue
class. Create a new instance for each value and pass in the SQL type
and parameter value in the constructor. You can also provide an
optional scale parameter for numeric values.For methods working with named parameters, you can use the
SqlParameterSource classes
BeanPropertySqlParameterSource or
MapSqlParameterSource. They both have methods
for registering the SQL type for any of the named parameter
values.Handling BLOB and CLOB objectsYou can store images and other binary objects as well and large
chunks of text. These large object are called BLOB for binary data and
CLOB for character data. Spring lets you handle these large objects
using the JdbcTemplate directly and also when using the higher
abstractions provided by RDBMS Objects and the SimpleJdbc classes. All
of these approaches use an implementation of the
LobHandler interface for the actual management of
the LOB data. The LobHandler provides access to a
LobCreator, via the
getLobCreator method, for creating new LOB
objects to be inserted.The LobCreator/LobHandler provides the
following support for LOB in- and output:BLOBbyte[] – getBlobAsBytes and setBlobAsBytesInputStream – getBlobAsBinaryStream and
setBlobAsBinaryStreamCLOBString – getClobAsString and setClobAsStringInputStream – getClobAsAsciiStream and
setClobAsAsciiStreamReader – getClobAsCharacterStream and
setClobAsCharacterStreamWe will now show an example of how to create and insert a BLOB. We
will later see how to read it back from the database.This example uses a JdbcTemplate and an implementation of the
AbstractLobCreatingPreparedStatementCallback. There is one method that
must be implemented and it is "setValues". In this method you will be
provided with a LobCreator that can be used to set the values for the
LOB columns in your SQL insert statement.We are assuming that we have a variable named 'lobHandler' that
already is set to an instance of a
DefaultLobHandler. This is typically done using
dependency injection.Here we use the lobHandler that in this example is a plain
DefaultLobHandlerUsing the method setClobAsCharacterStream
we pass in the contents of the CLOBUsing the method
setBlobAsBinartStream we pass in the
contents of the BLOBNow it's time to read the LOB data from the database. Again, we
use a JdbcTempate and we have the same instance variable 'lobHandler'
with a reference to a DefaultLobHandler.> l = jdbcTemplate.query("select id, a_clob, a_blob from lob_table",
new RowMapperUsing the method getClobAsString we
retrieve the contents of the CLOBUsing the method getBlobAsBytes we
retrieve the contents of the BLOBPassing in lists of values for IN clauseThe SQL standard allows for selecting rows based on an expression
that includes a variable list of values. A typical example would be
"select * from T_ACTOR where id in (1, 2, 3)". This variable list is not
directly supported for prepared statements by the JDBC standard - there
is no way of declaring a variable number of place holders. You would
have to either have a number of variations with the desired number of
place holders prepared or you would have to dynamically generate the SQL
string once you know how many place holders are required. The named
parameter support provided in the
NamedParameterJdbcTemplate and
SimpleJdbcTemplate takes the latter approach.
When you pass in the values you should pass them in as a
java.util.List of primitive objects. This list
will be used to insert the required place holders and pass in the values
during the statement execution.You need to be careful when passing in a large number of values.
The JDBC standard doesn't guarantee that you can use more than 100
values for an IN expression list. Various databases exceed this
number, but they usually have a hard limit for how many values are
allowed. Oracle's limit for instance is 1000.In addition to the primitive values in the value list, you can
create a java.util.List of object arrays. This
would support a case where there are multiple expressions defined for
the IN clause like "select * from T_ACTOR where (id, last_name) in ((1,
'Johnson'), (2, 'Harrop'))". This of course requires that your database
supports this syntax.Handling complex types for stored procedure callsWhen calling stored procedures it's sometimes possible to use
complex types specific to the database. To accommodate these types
Spring provides a SqlReturnType for handling them
when they are returned from the stored procedure call and
SqlTypeValue when they are passed in as a
parameter to the stored procedure.Here is an example of returning the value of an Oracle STRUCT
object of the user declared type "ITEM_TYPE". The
SqlReturnType interface has a single method named
"getTypeValue" that must be implemented. This
interface is used as part of the declaration of an
SqlOutParameter.Going from Java to the database and passing in the
value of a TestItem into a stored procedure is
done using the SqlTypeValue. The
SqlTypeValue interface has a single method named
"createTypeValue" that must be implemented. The
active connection is passed in and can be used to create database
specific objects like StructDescriptors or
ArrayDescriptorsThis SqlTypeValue can now be
added to the Map containing the input parameters for the execute call of
the stored procedure.Another use for the SqlTypeValue is for
passing in an array of values to an Oracle stored procedure. Oracle has
its own internal ARRAY class that must be used in
this case and we can use the SqlTypeValue to
create an instance of the Oracle ARRAY and
populate it with values from our Java array.Embedded database supportThe org.springframework.jdbc.datasource.embedded
package provides support for embedded Java database engines. Support for
HSQL, H2, and Derby is provided natively. There is
also an extensible API for plugging in new embedded database types and
DataSource implementations.Why use an embedded database?An embedded database is useful during the development phase of a
project due to its lightweight nature. Ease of configuration, quick
startup time, testability, and the ability to rapidly evolve SQL during
development are just some of the benefits of using an embedded
database.Creating an embedded database instance using Spring XMLWhen you wish to expose an embedded database instance as a bean in
a Spring ApplicationContext, use the embedded-database tag in the
spring-jdbc namespace:
]]>The configuration above creates an embedded HSQL database
populated with SQL from schema.sql and testdata.sql resources in the
classpath. The database instance is made available to the Spring
container as a bean of type javax.sql.DataSource.
This bean can then be injected into data access objects as
needed.Creating an embedded database instance programaticallyThe EmbeddedDatabaseBuilder class provides
a fluent API for constructing an embedded database programmatically. Use
this when you need to create an embedded database instance in a
standalone environment, such as a data access object unit test:
Extending the embedded database supportSpring Jdbc's embedded database support can be extended in two
ways: Implement EmbeddedDatabaseConfigurer
to support a new embedded database type, such as Apache
Derby.Implement DataSourceFactory to
support a new DataSource implementation, such as a connection
pool, to manage embedded database connections.You are encouraged to contribute back extensions to the Spring
community at jira.springframework.org.Using HSQL
Support for HSQL 1.0.0 to 2.0.0 is provided.
HSQL is the default database embedded database that will be used if no type is specified explicitly.
To specify HSQL explicitly, set the type attribute of the embedded-database tag to HSQL.
If using the builder API, call the type(EmbeddedDatabaseType) method with EmbeddedDatabaseType.HSQL.
Using H2
Support for H2 1.8.0 to 2.0.0 is provided.
To enable H2, set the type attribute of the embedded-database tag to H2.
If using the builder API, call the type(EmbeddedDatabaseType) method with EmbeddedDatabaseType.H2.
Using Derby
Support for Apache Derby 10.5.1.1 to 10.6.0 is provided.
To enable Derby, set the type attribute of the embedded-database tag to Derby.
If using the builder API, call the type(EmbeddedDatabaseType) method with EmbeddedDatabaseType.Derby.
Unit testing data access logic with an EmbeddedDatabase
Embedded databases provide a lightweight way to test data access code.
A basic data access unit test template that uses an embedded database is provided below: