spring-framework/spring-framework-reference/src/jdbc.xml

2704 lines
124 KiB
XML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<chapter id="jdbc">
<title>Data access using JDBC</title>
<section id="jdbc-introduction">
<title>Introduction</title>
<para>The 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):</para>
<orderedlist numeration="arabic">
<listitem>
<para>Define connection parameters</para>
</listitem>
<listitem>
<para>Open the connection</para>
</listitem>
<listitem>
<para><emphasis>Specify the statement</emphasis></para>
</listitem>
<listitem>
<para>Prepare and execute the statement</para>
</listitem>
<listitem>
<para>Set up the loop to iterate through the results (if any)</para>
</listitem>
<listitem>
<para><emphasis>Do the work for each iteration</emphasis></para>
</listitem>
<listitem>
<para>Process any exception</para>
</listitem>
<listitem>
<para>Handle transactions</para>
</listitem>
<listitem>
<para>Close the connection</para>
</listitem>
</orderedlist>
<para>The Spring Framework takes care of all the grungy, low-level details
that can make JDBC such a tedious API to develop with.</para>
<section id="jdbc-choose-style">
<title>Choosing a style</title>
<para>There 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.</para>
<itemizedlist>
<listitem>
<para><emphasis role="bold">JdbcTemplate</emphasis> - 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 vararg support.</para>
</listitem>
<listitem>
<para><emphasis role="bold">NamedParameterJdbcTemplate</emphasis> -
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 vararg support for Spring 3.0.</para>
</listitem>
<listitem>
<para><emphasis role="bold">SimpleJdbcTemplate</emphasis> - 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.</para>
</listitem>
<listitem>
<para><emphasis role="bold">SimpleJdbcInsert and
SimpleJdbcCall</emphasis> - 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.</para>
</listitem>
<listitem>
<para><emphasis role="bold">RDBMS Objects including MappingSqlQuery,
SqlUpdate and StoredProcedure</emphasis> - 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.</para>
</listitem>
</itemizedlist>
</section>
<section id="jdbc-packages">
<title>The package hierarchy</title>
<para>The Spring Framework's JDBC abstraction framework consists of four
different packages, namely <literal>core</literal>,
<literal>datasource</literal>, <literal>object</literal>, and
<literal>support</literal>.</para>
<para>The <literal>org.springframework.jdbc.core</literal> package
contains the <classname>JdbcTemplate</classname> class and its various
callback interfaces, plus a variety of related classes. A sub-package
named <literal>org.springframework.jdbc.core.simple</literal> contains
the <classname>SimpleJdbcTemplate</classname> class and the related
<classname>SimpleJdbcInsert</classname> and
<classname>SimpleJdbcCall</classname> classes. Another sub-package named
<literal>org.springframework.jdbc.core.namedparam</literal> contains the
<classname>NamedParameterJdbcTemplate</classname> class and the related
support classes.</para>
<para>The <literal>org.springframework.jdbc.datasource</literal> package
contains a utility class for easy
<interfacename>DataSource</interfacename> access, and various simple
<interfacename>DataSource</interfacename> implementations that can be
used for testing and running unmodified JDBC code outside of a Java EE
container. A sub-package named
<literal>org.springfamework.jdbc.datasource.embedded</literal> provides
support for creating in-memory database instances using Java database
engines such as HSQL and H2.</para>
<para>Next, the <literal>org.springframework.jdbc.object</literal>
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
<quote>disconnected</quote> from the database. This higher level of JDBC
abstraction depends on the lower-level abstraction in the
<literal>org.springframework.jdbc.core</literal> package.</para>
<para>Finally the <literal>org.springframework.jdbc.support</literal>
package is where you find the <classname>SQLException</classname>
translation functionality and some utility classes.</para>
<para>Exceptions thrown during JDBC processing are translated to
exceptions defined in the <literal>org.springframework.dao</literal>
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.</para>
</section>
</section>
<section id="jdbc-core">
<title>Using the JDBC Core classes to control basic JDBC processing and
error handling</title>
<section id="jdbc-JdbcTemplate">
<title><classname>JdbcTemplate</classname></title>
<para>The <classname>JdbcTemplate</classname> 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 <interfacename>ResultSet</interfacename>s and extraction
of returned parameter values. It also catches JDBC exceptions and
translates them to the generic, more informative, exception hierarchy
defined in the <literal>org.springframework.dao</literal>
package.</para>
<para>Code using the <classname>JdbcTemplate</classname> only need to
implement callback interfaces, giving them a clearly defined contract.
The <interfacename>PreparedStatementCreator</interfacename> callback
interface creates a prepared statement given a
<interfacename>Connection</interfacename> provided by this class,
providing SQL and any necessary parameters. The same is true for the
<interfacename>CallableStatementCreator</interfacename> interface which
creates callable statement. The
<interfacename>RowCallbackHandler</interfacename> interface extracts
values from each row of a
<interfacename>ResultSet</interfacename>.</para>
<para>The <classname>JdbcTemplate</classname> can be used within a DAO
implementation via direct instantiation with a
<interfacename>DataSource</interfacename> reference, or be configured in
a Spring IOC container and given to DAOs as a bean reference. Note: the
<interfacename>DataSource</interfacename> 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.</para>
<para>Finally, all of the SQL issued by this class is logged at the
<literal>'DEBUG'</literal> level under the category corresponding to the
fully qualified class name of the template instance (typically
<classname>JdbcTemplate</classname>, but it may be different if a custom
subclass of the <classname>JdbcTemplate</classname> class is being
used).</para>
<section id="jdbc-JdbcTemplate-examples">
<title>Examples</title>
<para>Find below some examples of using the
<classname>JdbcTemplate</classname> class. (These examples are not an
exhaustive list of all of the functionality exposed by the
<classname>JdbcTemplate</classname>; see the attendant Javadocs for
that).</para>
<section id="jdbc-JdbcTemplate-examples-query">
<title>Querying (SELECT)</title>
<para>A simple query for getting the number of rows in a
relation.</para>
<programlisting language="java"><![CDATA[int rowCount = this.jdbcTemplate.queryForInt("select count(*) from t_actor");]]></programlisting>
<para>A simple query using a bind variable.</para>
<programlisting language="java"><![CDATA[int countOfActorsNamedJoe = this.jdbcTemplate.queryForInt(
"select count(*) from t_actor where first_name = ?", "Joe");]]></programlisting>
<para>Querying for a <classname>String</classname>.</para>
<programlisting language="java"><![CDATA[String lastName = this.jdbcTemplate.queryForObject(
"select last_name from t_actor where id = ?",
new Object[]{1212L}, String.class);]]></programlisting>
<para>Querying and populating a <emphasis>single</emphasis> domain
object.</para>
<programlisting language="java"><![CDATA[Actor actor = this.jdbcTemplate.queryForObject(
"select first_name, last_name from t_actor where id = ?",
new Object[]{1212L},
new RowMapper<Actor>() {
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;
}
});
]]></programlisting>
<para>Querying and populating a number of domain objects.</para>
<programlisting language="java"><![CDATA[List<Actor> actors = this.jdbcTemplate.query(
"select first_name, last_name from t_actor",
new RowMapper<Actor>() {
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;
}
});
]]></programlisting>
<para>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 <interfacename>RowMapper</interfacename> anonymous inner
classes, and extract them out into a single class (typically a
<literal>static</literal> 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:</para>
<programlisting language="java"><![CDATA[public List<Actor> findAllActors() {
return this.jdbcTemplate.query( "select first_name, last_name from t_actor", new ActorMapper());
}
private static final class ActorMapper implements RowMapper<Actor> {
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;
}
}]]></programlisting>
</section>
<section id="jdbc-JdbcTemplate-examples-update">
<title>Updating (INSERT/UPDATE/DELETE)</title>
<programlisting language="java"><![CDATA[this.jdbcTemplate.update(
"insert into t_actor (first_name, last_name) values (?, ?)",
"Leonor", "Watling");]]></programlisting>
<programlisting language="java"><![CDATA[this.jdbcTemplate.update(
"update t_actor set = ? where id = ?",
"Banjo", 5276L);]]></programlisting>
<programlisting language="java"><![CDATA[this.jdbcTemplate.update(
"delete from actor where id = ?",
Long.valueOf(actorId));]]></programlisting>
</section>
<section id="jdbc-JdbcTemplate-examples-other">
<title>Other operations</title>
<para>The <methodname>execute(..)</methodname> 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.</para>
<programlisting language="java"><![CDATA[this.jdbcTemplate.execute("create table mytable (id integer, name varchar(100))");]]></programlisting>
<para>Invoking a simple stored procedure (more sophisticated stored
procedure support is <link linkend="jdbc-StoredProcedure">covered
later</link>).</para>
<programlisting language="java"><![CDATA[this.jdbcTemplate.update(
"call SUPPORT.REFRESH_ACTORS_SUMMARY(?)",
Long.valueOf(unionId));]]></programlisting>
</section>
</section>
<section id="jdbc-JdbcTemplate-idioms">
<title><classname>JdbcTemplate</classname> idioms (best
practices)</title>
<para>Instances of the <classname>JdbcTemplate</classname> class are
<emphasis>threadsafe once configured</emphasis>. This is important
because it means that you can configure a single instance of a
<classname>JdbcTemplate</classname> and then safely inject this
<emphasis>shared</emphasis> reference into multiple DAOs (or
repositories). To be clear, the <classname>JdbcTemplate</classname> is
stateful, in that it maintains a reference to a
<interfacename>DataSource</interfacename>, but this state is
<emphasis>not</emphasis> conversational state.</para>
<para>A common idiom when using the
<classname>JdbcTemplate</classname> class (and the associated <link
linkend="jdbc-SimpleJdbcTemplate"><classname>SimpleJdbcTemplate</classname></link>
and <link
linkend="jdbc-NamedParameterJdbcTemplate"><classname>NamedParameterJdbcTemplate</classname></link>
classes) is to configure a <interfacename>DataSource</interfacename>
in your Spring configuration file, and then dependency inject that
shared <interfacename>DataSource</interfacename> bean into your DAO
classes; the <classname>JdbcTemplate</classname> is created in the
setter for the <interfacename>DataSource</interfacename>. This leads
to DAOs that look in part like this:</para>
<programlisting language="java">public class JdbcCorporateEventDao implements CorporateEventDao {
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
<emphasis role="bold">this.jdbcTemplate = new JdbcTemplate(dataSource);</emphasis>
}
<lineannotation>// JDBC-backed implementations of the methods on the <interfacename>CorporateEventDao</interfacename> follow...</lineannotation>
}</programlisting>
<para>The corresponding configuration might look like this.</para>
<programlisting language="xml"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<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"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<bean id="corporateEventDao" class="com.example.JdbcCorporateEventDao">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<context:property-placeholder location="jdbc.properties"/>
</beans>]]></programlisting>
<para>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
<classname>DataSource</classname> with the
<interfacename>@Autowired</interfacename> annotation.</para>
<para><programlisting language="java">public class JdbcCorporateEventDao implements CorporateEventDao {
private JdbcTemplate jdbcTemplate;
@Autowired
public void setDataSource(DataSource dataSource) {
<emphasis role="bold">this.jdbcTemplate = new JdbcTemplate(dataSource);</emphasis>
}
<lineannotation>// JDBC-backed implementations of the methods on the <interfacename>CorporateEventDao</interfacename> follow...</lineannotation>
}</programlisting></para>
<para>The corresponding XML configuration file would look like the
following:</para>
<para><programlisting language="xml"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<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"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<!-- Scans within the base package of the application for @Components to configure as beans -->
<context:component-scan base-package="org.springframework.docs.test" />
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<context:property-placeholder location="jdbc.properties"/>
</beans>]]></programlisting>If you are using Spring's
<classname>JdbcDaoSupport</classname> class, and your various
JDBC-backed DAO classes extend from it, then you inherit a
<methodname>setDataSource(..)</methodname> 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 <classname>JdbcDaoSupport</classname> class you will
see that there is not a whole lot to it... it is provided as a
convenience only.</para>
<para>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 <classname>JdbcTemplate</classname>
class each and every time you wish to execute some SQL... remember,
once configured, a <classname>JdbcTemplate</classname> instance is
threadsafe. A reason for wanting multiple
<classname>JdbcTemplate</classname> instances would be when you have
an application that accesses multiple databases, which requires
multiple <interfacename>DataSources</interfacename>, and subsequently
multiple differently configured
<classname>JdbcTemplates</classname>.</para>
</section>
</section>
<section id="jdbc-NamedParameterJdbcTemplate">
<title><classname>NamedParameterJdbcTemplate</classname></title>
<para>The <classname>NamedParameterJdbcTemplate</classname> class adds
support for programming JDBC statements using named parameters (as
opposed to programming JDBC statements using only classic placeholder
(<literal>'?'</literal>) arguments. The
<classname>NamedParameterJdbcTemplate</classname> class wraps a
<classname>JdbcTemplate</classname>, and delegates to the wrapped
<classname>JdbcTemplate</classname> to do much of its work. This section
will describe only those areas of the
<classname>NamedParameterJdbcTemplate</classname> class that differ from
the <classname>JdbcTemplate</classname> itself; namely, programming JDBC
statements using named parameters.</para>
<programlisting language="java"><lineannotation>// some JDBC-backed DAO class...</lineannotation>
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);
}</programlisting>
<para>Notice the use of the named parameter notation in the value
assigned to the <literal>'sql'</literal> variable, and the corresponding
value that is plugged into the <literal>'namedParameters'</literal>
variable (of type <classname>MapSqlParameterSource</classname>).</para>
<para>If you like, you can also pass along named parameters (and their
corresponding values) to a
<classname>NamedParameterJdbcTemplate</classname> instance using the
(perhaps more familiar) <interfacename>Map</interfacename>-based style.
(The rest of the methods exposed by the
<interfacename>NamedParameterJdbcOperations</interfacename> - and
implemented by the <classname>NamedParameterJdbcTemplate</classname>
class) follow a similar pattern and will not be covered here.)</para>
<programlisting language="java"><lineannotation>// some JDBC-backed DAO class...</lineannotation>
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);
}</programlisting>
<para>Another nice feature related to the
<classname>NamedParameterJdbcTemplate</classname> (and existing in the
same Java package) is the
<interfacename>SqlParameterSource</interfacename> interface. You have
already seen an example of an implementation of this interface in one of
the preceding code snippets (the
<classname>MapSqlParameterSource</classname> class). The entire point of
the <interfacename>SqlParameterSource</interfacename> is to serve as a
source of named parameter values to a
<classname>NamedParameterJdbcTemplate</classname>. The
<classname>MapSqlParameterSource</classname> class is a very simple
implementation, that is simply an adapter around a
<interfacename>java.util.Map</interfacename>, where the keys are the
parameter names and the values are the parameter values.</para>
<para>Another <interfacename>SqlParameterSource</interfacename>
implementation is the
<classname>BeanPropertySqlParameterSource</classname> class. This class
wraps an arbitrary JavaBean (that is, an instance of a class that
adheres to <ulink
url="http://java.sun.com/products/javabeans/docs/spec.html">the JavaBean
conventions</ulink>), and uses the properties of the wrapped JavaBean as
the source of named parameter values.</para>
<programlisting language="java">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;
}
<lineannotation>// setters omitted...</lineannotation>
}</programlisting>
<programlisting language="java"><lineannotation>// some JDBC-backed DAO class...</lineannotation>
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
}
public int countOfActors(Actor exampleActor) {
<lineannotation>// notice how the named parameters match the properties of the above '<classname>Actor</classname>' class</lineannotation>
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);
}</programlisting>
<para>Remember that the
<classname>NamedParameterJdbcTemplate</classname> class
<emphasis>wraps</emphasis> a classic <classname>JdbcTemplate</classname>
template; if you need access to the wrapped
<classname>JdbcTemplate</classname> instance (to access some of the
functionality only present in the <classname>JdbcTemplate</classname>
class), then you can use the
<methodname>getJdbcOperations()</methodname> method to access the
wrapped <classname>JdbcTemplate</classname> <emphasis>via the
<interfacename>JdbcOperations</interfacename>
interface</emphasis>.</para>
<para>See also the section entitled <xref
linkend="jdbc-JdbcTemplate-idioms" /> for some advice on how to best use
the <classname>NamedParameterJdbcTemplate</classname> class in the
context of an application.</para>
</section>
<section id="jdbc-SimpleJdbcTemplate">
<title><classname>SimpleJdbcTemplate</classname></title>
<note>
<para><emphasis>The idea behind the
<classname>SimpleJdbcTemplate</classname> is to provide a simpler
interface that takes better advantage of Java 5 features. It was
initially the only <interfacename>JdbcOperations</interfacename>
implementation that provided support for Java 5 enhanced syntax with
generics and varargs. Now, in Spring 3.0, the original
<classname>JdbcTemplate</classname> has been upgraded to Java 5 as
well, but the <classname>SimpleJdbcTemplate</classname> still has the
advantage of providing a simpler API when you don't need access to all
the methods that the <classname>JdbcTemplate</classname> offers. Also,
since the <classname>SimpleJdbcTemplate</classname> was designed for
Java 5 there are more methods that take advantage of varargs due to
different ordering of the parameters.</emphasis></para>
</note>
<para>The <classname>SimpleJdbcTemplate</classname> class is a wrapper
around the classic <classname>JdbcTemplate</classname> that takes better
advantage of Java 5 language features such as varargs and
autoboxing.</para>
<para>The value-add of the <classname>SimpleJdbcTemplate</classname>
class in the area of syntactic-sugar is best illustrated with a
<emphasis>'before and after'</emphasis> example. The following code
snippet shows first some data access code using the classic
<classname>JdbcTemplate</classname>, followed immediately thereafter by
a code snippet that does the same job, only this time using the
<classname>SimpleJdbcTemplate</classname>.</para>
<programlisting language="java"><lineannotation>// classic <classname>JdbcTemplate</classname>-style...</lineannotation>
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&lt;Actor&gt; mapper = new RowMapper&lt;Actor&gt;() {
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;
}
};
<lineannotation>// notice the wrapping up of the argumenta in an array</lineannotation>
return (Actor) jdbcTemplate.queryForObject(sql, new Object[] {id}, mapper);
}</programlisting>
<para>Here is the same method, only this time using the
<classname>SimpleJdbcTemplate</classname>.</para>
<programlisting language="java"><lineannotation>// <classname>SimpleJdbcTemplate</classname>-style...</lineannotation>
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&lt;Actor&gt; mapper = new RowMapper&lt;Actor&gt;() {
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;
}
};
<lineannotation>// notice the use of varargs since the parameter values now come
// after the RowMapper parameter</lineannotation>
return this.simpleJdbcTemplate.queryForObject(sql, mapper, specialty, age);
}</programlisting>
<para>See also the section entitled <xref
linkend="jdbc-JdbcTemplate-idioms" /> for some advice on how to best use
the <classname>SimpleJdbcTemplate</classname> class in the context of an
application.</para>
<note>
<para>The <classname>SimpleJdbcTemplate</classname> class only offers
a subset of the methods exposed on the
<classname>JdbcTemplate</classname> class. If you need to use a method
from the <classname>JdbcTemplate</classname> that is not defined on
the <classname>SimpleJdbcTemplate</classname>, you can always access
the underlying <classname>JdbcTemplate</classname> by calling the
<methodname>getJdbcOperations()</methodname> method on the
<classname>SimpleJdbcTemplate</classname>, which will then allow you
to invoke the method that you want. The only downside is that the
methods on the <interfacename>JdbcOperations</interfacename> interface
are not generified, so you are back to casting and such again.</para>
</note>
</section>
<section id="jdbc-datasource">
<title><interfacename>DataSource</interfacename></title>
<para>In 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
<interfacename>DataSource</interfacename>. A
<interfacename>DataSource</interfacename> 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.</para>
<para>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.</para>
<para>We will use the <classname>DriverManagerDataSource</classname>
implementation for this section but there are several additional
implementations that will be covered later on. The
<classname>DriverManagerDataSource</classname> 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 <classname>DriverManager</classname> 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
<classname>DriverManagerDataSource</classname> in Java code:</para>
<programlisting language="java"><![CDATA[DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
dataSource.setUrl("jdbc:hsqldb:hsql://localhost:");
dataSource.setUsername("sa");
dataSource.setPassword("");]]></programlisting>
<para>We also have an example how this would look in an XML
configuration:</para>
<programlisting language="java"><![CDATA[<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<context:property-placeholder location="jdbc.properties"/>]]></programlisting>
<note>
<para>The <classname>DriverManagerDataSource</classname> 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.</para>
</note>
<para>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.</para>
<para>FIrst we have the DBCP configuration:</para>
<programlisting language="java"><![CDATA[<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<context:property-placeholder location="jdbc.properties"/>]]></programlisting>
<para>And last we have the C3P0 configuration:</para>
<programlisting language="java"><![CDATA[<bean id="dataSource"
class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="${jdbc.driverClassName}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<context:property-placeholder location="jdbc.properties"/>]]></programlisting>
</section>
<section id="jdbc-SQLExceptionTranslator">
<title><interfacename>SQLExceptionTranslator</interfacename></title>
<para><interfacename>SQLExceptionTranslator</interfacename> is an
interface to be implemented by classes that can translate between
<classname>SQLExceptions</classname> and Spring's own
data-access-strategy-agnostic
<classname>org.springframework.dao.DataAccessException</classname>.
Implementations can be generic (for example, using SQLState codes for
JDBC) or proprietary (for example, using Oracle error codes) for greater
precision.</para>
<para><classname>SQLErrorCodeSQLExceptionTranslator</classname> is the
implementation of <interfacename>SQLExceptionTranslator</interfacename>
that is used by default. This implementation uses specific vendor codes.
More precise than <literal>SQLState</literal> implementation, but vendor
specific. The error code translations are based on codes held in a
JavaBean type class named <classname>SQLErrorCodes</classname>. This
class is created and populated by an
<classname>SQLErrorCodesFactory</classname> which as the name suggests
is a factory for creating <classname>SQLErrorCodes</classname> based on
the contents of a configuration file named <filename
class="libraryfile">'sql-error-codes.xml'</filename>. This file is
populated with vendor codes and based on the DatabaseProductName taken
from the <interfacename>DatabaseMetaData</interfacename>, the codes for
the current database are used.</para>
<para>The <classname>SQLErrorCodeSQLExceptionTranslator</classname>
applies the following matching rules: <itemizedlist spacing="compact">
<listitem>
<para>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.</para>
</listitem>
<listitem>
<para>Apply error code matching. Error codes are obtained from the
<classname>SQLErrorCodesFactory</classname> by default. This looks
up error codes from the classpath and keys into them from the
database name from the database metadata.</para>
</listitem>
<listitem>
<para>Use the fallback translator.
<classname>SQLStateSQLExceptionTranslator</classname> is the
default fallback translator.</para>
</listitem>
</itemizedlist></para>
<para><classname>SQLErrorCodeSQLExceptionTranslator</classname> can be
extended the following way:</para>
<programlisting language="java"><![CDATA[public class CustomSQLErrorCodesTranslator extends SQLErrorCodeSQLExceptionTranslator {
protected DataAccessException customTranslate(String task, String sql, SQLException sqlex) {
if (sqlex.getErrorCode() == -12345) {
return new DeadlockLoserDataAccessException(task, sqlex);
}
return null;
}
}]]></programlisting>
<para>In this example the specific error code
<literal>'-12345'</literal> 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
<classname>JdbcTemplate</classname> using the method
<literal>setExceptionTranslator</literal> and to use this
<classname>JdbcTemplate</classname> for all of the data access
processing where this translator is needed. Here is an example of how
this custom translator can be used:</para>
<programlisting language="java"><lineannotation>private JdbcTemplate jdbcTemoplate;
public void setDataSource(DataSource dataSource) {
// create a <classname>JdbcTemplate</classname> and set data source</lineannotation>
this.jdbcTemplate = new JdbcTemplate();
this.jdbcTemplate.setDataSource(dataSource);
<lineannotation> // create a custom translator and set the <interfacename>DataSource</interfacename> for the default translation lookup</lineannotation>
CustomSQLErrorCodesTranslator tr = new CustomSQLErrorCodesTranslator();
tr.setDataSource(dataSource);
this.jdbcTemplate.setExceptionTranslator(tr);
}
<lineannotation>public void updateShippingCharge(long orderId, long pct) {
// use the <classname>prepared JdbcTemplate</classname> for this u<classname>pdate</classname></lineannotation>
this.jdbcTemplate.update(
"update orders" +
" set shipping_charge = shipping_charge * ? / 100" +
" where id = ?"
pct, orderId);
}</programlisting>
<para>The custom translator is passed a data source because we still
want the default translation to look up the error codes in
<literal>sql-error-codes.xml</literal>.</para>
</section>
<section id="jdbc-statements-executing">
<title>Executing statements</title>
<para>To execute an SQL statement, there is very little code needed. All
you need is a <interfacename>DataSource</interfacename> and a
<classname>JdbcTemplate</classname>. Once you have that, you can use a
number of convenience methods that are provided with the
<classname>JdbcTemplate</classname>. Here is a short example showing
what you need to include for a minimal but fully functional class that
creates a new table.</para>
<programlisting language="java"><![CDATA[import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
public class ExecuteAStatement {
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
public void doExecute() {
this.jdbcTemplate.execute("create table mytable (id integer, name varchar(100))");
}
}]]></programlisting>
</section>
<section id="jdbc-statements-querying">
<title>Running Queries</title>
<para>In 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
<methodname>queryForInt(..)</methodname>,
<methodname>queryForLong(..)</methodname> or
<methodname>queryForObject(..)</methodname>. The latter will convert the
returned JDBC <classname>Type</classname> to the Java class that is
passed in as an argument. If the type conversion is invalid, then an
<exceptionname>InvalidDataAccessApiUsageException</exceptionname> will
be thrown. Here is an example that contains two query methods, one for
an <classname>int</classname> and one that queries for a
<classname>String</classname>.</para>
<programlisting language="java"><![CDATA[import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
public class RunAQuery {
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
public int getCount() {
return this.jdbcTemplate.queryForInt("select count(*) from mytable");
}
public String getName() {
return (String) this.jdbcTemplate.queryForObject("select name from mytable", String.class);
}
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
}]]></programlisting>
<para>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
<methodname>queryForList(..)</methodname> which returns a
<interfacename>List</interfacename> where each entry is a
<interfacename>Map</interfacename> 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:</para>
<programlisting language="java"><![CDATA[
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
public List<Map<String, Object>> getList() {
return this.jdbcTemplate.queryForList("select * from mytable");
}]]></programlisting>
<para>The list returned would look something like this:</para>
<programlisting><![CDATA[[{name=Bob, id=1}, {name=Mary, id=2}]]]></programlisting>
</section>
<section id="jdbc-updates">
<title>Updating the database</title>
<para>There 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).</para>
<programlisting language="java"><![CDATA[import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
public class ExecuteAnUpdate {
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
public void setName(int id, String name) {
this.jdbcTemplate.update(
"update mytable set name = ? where id = ?",
name, id);
}
}]]></programlisting>
</section>
<section id="jdbc-auto-genereted-keys">
<title>Retrieving auto-generated keys</title>
<para>One of the <methodname>update</methodname> 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
<classname>PreparedStatementCreator</classname> as its first argument,
and this is the way the required insert statement is specified. The
other argument is a <classname>KeyHolder</classname>, which will contain
the generated key on successful return from the update. There is not a
standard single way to create an appropriate
<classname>PreparedStatement</classname> (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:</para>
<programlisting language="java">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);
<lineannotation>// keyHolder.getKey() now contains the generated key</lineannotation></programlisting>
</section>
</section>
<section id="jdbc-connections">
<title>Controlling database connections</title>
<section id="jdbc-DataSourceUtils">
<title><classname>DataSourceUtils</classname></title>
<para>The <classname>DataSourceUtils</classname> class is a convenient
and powerful helper class that provides <literal>static</literal>
methods to obtain connections from JNDI and close connections if
necessary. It has support for thread-bound connections, for example for
use with <classname>DataSourceTransactionManager</classname>.</para>
</section>
<section id="jdbc-SmartDataSource">
<title><interfacename>SmartDataSource</interfacename></title>
<para>The <interfacename>SmartDataSource</interfacename> interface is to
be implemented by classes that can provide a connection to a relational
database. Extends the <interfacename>DataSource</interfacename>
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.</para>
</section>
<section id="jdbc-AbstractDataSource">
<title><classname>AbstractDataSource</classname></title>
<para>This is an <literal>abstract</literal> base class for Spring's
<interfacename>DataSource</interfacename> implementations, that takes
care of the "uninteresting" glue. This is the class one would extend if
one was writing one's own <interfacename>DataSource</interfacename>
implementation.</para>
</section>
<section id="jdbc-SingleConnectionDataSource">
<title><classname>SingleConnectionDataSource</classname></title>
<para>The <classname>SingleConnectionDataSource</classname> class is an
implementation of the <interfacename>SmartDataSource</interfacename>
interface that wraps a <emphasis>single</emphasis>
<interfacename>Connection</interfacename> that is
<emphasis>not</emphasis> closed after use. Obviously, this is not
multi-threading capable.</para>
<para>If client code will call close in the assumption of a pooled
connection, like when using persistence tools, set
<literal>suppressClose</literal> to <literal>true</literal>. 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
<interfacename>Connection</interfacename> or the like anymore.</para>
<para>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
<classname>DriverManagerDataSource</classname>, it reuses the same
connection all the time, avoiding excessive creation of physical
connections.</para>
</section>
<section id="jdbc-DriverManagerDataSource">
<title><classname>DriverManagerDataSource</classname></title>
<para>The <classname>DriverManagerDataSource</classname> class is an
implementation of the standard <interfacename>DataSource</interfacename>
interface that configures a plain old JDBC Driver via bean properties,
and returns a new <interfacename>Connection</interfacename> every
time.</para>
<para>This is potentially useful for test or standalone environments
outside of a J2EE container, either as a
<interfacename>DataSource</interfacename> bean in a Spring IoC
container, or in conjunction with a simple JNDI environment.
Pool-assuming <literal>Connection.close()</literal> calls will simply
close the connection, so any
<interfacename>DataSource</interfacename>-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
<classname>DriverManagerDataSource</classname>.</para>
</section>
<section id="jdbc-TransactionAwareDataSourceProxy">
<title><classname>TransactionAwareDataSourceProxy</classname></title>
<para><classname>TransactionAwareDataSourceProxy</classname> is a proxy
for a target <interfacename>DataSource</interfacename>, which wraps that
target <interfacename>DataSource</interfacename> to add awareness of
Spring-managed transactions. In this respect it is similar to a
transactional JNDI <interfacename>DataSource</interfacename> as provided
by a J2EE server.</para>
<note>
<para>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 <interfacename>DataSource</interfacename>
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
<classname>JdbcTemplate</classname> or
<classname>DataSourceUtils</classname>.</para>
</note>
<para><emphasis>(See the
<classname>TransactionAwareDataSourceProxy</classname> Javadocs for more
details.)</emphasis></para>
</section>
<section id="jdbc-DataSourceTransactionManager">
<title><classname>DataSourceTransactionManager</classname></title>
<para>The <classname>DataSourceTransactionManager</classname> class is a
<interfacename>PlatformTransactionManager</interfacename> 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.</para>
<para>Application code is required to retrieve the JDBC connection via
<literal>DataSourceUtils.getConnection(DataSource)</literal> instead of
J2EE's standard <literal>DataSource.getConnection</literal>. This is
recommended anyway, as it throws unchecked
<literal>org.springframework.dao</literal> exceptions instead of checked
<exceptionname>SQLExceptions</exceptionname>. All framework classes like
<classname>JdbcTemplate</classname> 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.</para>
<para>The <classname>DataSourceTransactionManager</classname> 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 <classname>JdbcTemplate</classname> or
call <literal>DataSourceUtils.applyTransactionTimeout(..)</literal>
method for each created statement.</para>
<para>This implementation can be used instead of
<classname>JtaTransactionManager</classname> 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!</para>
</section>
<section id="jdbc-NativeJdbcExtractor">
<title>NativeJdbcExtractor</title>
<para>There 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
<classname>DataSource</classname> that wraps the
<classname>Connection</classname>, <classname>Statement</classname> and
<classname>ResultSet</classname> objects with its own wrapper objects.
To gain access to the native objects you can configure your
<classname>JdbcTemplate</classname> or
<classname>OracleLobHandler</classname> with a
<classname>NativeJdbcExtractor</classname>.</para>
<para>The NativeJdbcExtractor comes in a variety of flavors to match
your execution environment:</para>
<itemizedlist>
<listitem>
<para>SimpleNativeJdbcExtractor</para>
</listitem>
<listitem>
<para>C3P0NativeJdbcExtractor</para>
</listitem>
<listitem>
<para>CommonsDbcpNativeJdbcExtractor</para>
</listitem>
<listitem>
<para>JBossNativeJdbcExtractor</para>
</listitem>
<listitem>
<para>WebLogicNativeJdbcExtractor</para>
</listitem>
<listitem>
<para>WebSphereNativeJdbcExtractor</para>
</listitem>
<listitem>
<para>XAPoolNativeJdbcExtractor</para>
</listitem>
</itemizedlist>
<para>Usually the <classname>SimpleNativeJdbcExtractor</classname> is
sufficient for unwrapping a <classname>Connection</classname> object in
most environments. See the Java Docs for more details.</para>
</section>
</section>
<section id="jdbc-advanced-jdbc">
<title>JDBC batch operations</title>
<para>Most 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.</para>
<section id="jdbc-advanced-classic">
<title>Batch operations with the JdbcTemplate</title>
<para>Using the JdbcTemplate batch processing is accomplished by
implementing a special interface,
<classname>BatchPreparedStatementSetter</classname>, and passing that in
as the second parameter in your <classname>batchUpdate</classname>
method call. This interface has two methods you must implement. One is
named <classname>getBatchSize</classname> and here you provide the size
of the current batch. The other method is
<classname>setValues</classname> 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
<classname>getBatchSize</classname> 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.</para>
<para><programlisting language="java"><![CDATA[public class JdbcActorDao implements ActorDao {
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
public int[] batchUpdate(final List<Actor> 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
}]]></programlisting>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
<classname>InterruptibleBatchPreparedStatementSetter</classname>
interface which allows you to interrupt a batch once the input source is
exhausted. The <classname>isBatchExhausted</classname> method allows you
to signal the end of the batch.</para>
</section>
<section id="jdbc-advanced-simple">
<title>Batch operations with the SimpleJdbcTemplate</title>
<para>The <classname>SimpleJdbcTemplate</classname> 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 <classname>SqlParameterSource</classname>, one entry for each member
of the batch. You can use the
<classname>SqlParameterSource.createBatch</classname> method to create
this array, passing in either an array of JavaBeans or an array of Maps
containing the parameter values.</para>
<para>This example shows a batch update using named parameters:</para>
<para><programlisting language="java"><![CDATA[public class JdbcActorDao implements ActorDao {
private SimpleJdbcTemplate simpleJdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource);
}
public int[] batchUpdate(final List<Actor> 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
}]]></programlisting>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.</para>
<para>The same example using classic JDBC "?" place holders:</para>
<para><programlisting language="java"><![CDATA[public class JdbcActorDao implements ActorDao {
private SimpleJdbcTemplate simpleJdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource);
}
public int[] batchUpdate(final List<Actor> actors) {
List<Object[]> batch = new ArrayList<Object[]>();
for (Actor actor : actors) {
Object[] values = new Object[] {
actor.getFirstName(),
actor.getLastName(),
actor.getId()};
batch.add(values);
}
int[] updateCounts = simpleJdbcTemplate.batchUpdate(
"update t_actor set first_name = ?, last_name = ? where id = ?",
batch);
return updateCounts;
}
// ... additional methods
}]]></programlisting>All 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.</para>
</section>
</section>
<section id="jdbc-simple-jdbc">
<title>Simplifying JDBC operations with the SimpleJdbc classes</title>
<para>The <classname>SimpleJdbcInsert</classname> and
<classname>SimpleJdbcCall</classname> 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.</para>
<section id="jdbc-simple-jdbc-insert-1">
<title>Inserting data using SimpleJdbcInsert</title>
<para>Let's start by looking at the
<classname>SimpleJdbcInsert</classname> class first. We will use the
minimal amount of configuration options to start with. The
<classname>SimpleJdbcInsert</classname> should be instantiated in the
data access layer's initialization method. For this example, the
initializing method is the <classname>setDataSource</classname> method.
There is no need to subclass the <classname>SimpleJdbcInsert</classname>
class, just create a new instance and set the table name using the
<classname>withTableName</classname> method. Configuration methods for
this class follows the "fluid" style returning the instance of the
<classname>SimpleJdbcInsert</classname> 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.</para>
<programlisting language="java"><![CDATA[public class JdbcActorDao implements ActorDao {
private SimpleJdbcTemplate simpleJdbcTemplate;
private SimpleJdbcInsert insertActor;
public void setDataSource(DataSource dataSource) {
this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource);
this.insertActor =
new SimpleJdbcInsert(dataSource).withTableName("t_actor");
}
public void add(Actor actor) {
Map<String, Object> parameters = new HashMap<String, Object>(3);
parameters.put("id", actor.getId());
parameters.put("first_name", actor.getFirstName());
parameters.put("last_name", actor.getLastName());
insertActor.execute(parameters);
}
// ... additional methods
}]]></programlisting>
<para>The execute method used here takes a plain
<classname>java.utils.Map</classname> 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.</para>
</section>
<section id="jdbc-simple-jdbc-insert-2">
<title>Retrieving auto-generated keys using SimpleJdbcInsert</title>
<para>Next 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 <classname>SimpleJdbcInsert</classname>, in
addition to specifying the table name, we specify the name of the
generated key column using the
<classname>usingGeneratedKeyColumns</classname> method.</para>
<para><programlisting language="java"><![CDATA[public class JdbcActorDao implements ActorDao {
private SimpleJdbcTemplate simpleJdbcTemplate;
private SimpleJdbcInsert insertActor;
public void setDataSource(DataSource dataSource) {
this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource);
this.insertActor =
new SimpleJdbcInsert(dataSource)
.withTableName("t_actor")
.usingGeneratedKeyColumns("id");
}
public void add(Actor actor) {
Map<String, Object> parameters = new HashMap<String, Object>(2);
parameters.put("first_name", actor.getFirstName());
parameters.put("last_name", actor.getLastName());
Number newId = insertActor.executeAndReturnKey(parameters);
actor.setId(newId.longValue());
}
// ... additional methods
}]]></programlisting>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
<literal>executeReturningKey</literal> method. This returns a
<literal>java.lang.Number</literal> 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, <literal>java.lang.Number</literal> 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
<literal>KeyHolder</literal> that is returned from the
<literal>executeReturningKeyHolder</literal> method.</para>
</section>
<section id="jdbc-simple-jdbc-insert-3">
<title>Specifying the columns to use for a SimpleJdbcInsert</title>
<para>It'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 <classname>usingColumns</classname> method.</para>
<para><programlisting language="java"><![CDATA[public class JdbcActorDao implements ActorDao {
private SimpleJdbcTemplate simpleJdbcTemplate;
private SimpleJdbcInsert insertActor;
public void setDataSource(DataSource dataSource) {
this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource);
this.insertActor =
new SimpleJdbcInsert(dataSource)
.withTableName("t_actor")
.usingColumns("first_name", "last_name")
.usingGeneratedKeyColumns("id");
}
public void add(Actor actor) {
Map<String, Object> parameters = new HashMap<String, Object>(2);
parameters.put("first_name", actor.getFirstName());
parameters.put("last_name", actor.getLastName());
Number newId = insertActor.executeAndReturnKey(parameters);
actor.setId(newId.longValue());
}
// ... additional methods
}]]></programlisting>The execution of the insert is the same as if we had
relied on the metadata for determining what columns to use.</para>
</section>
<section id="jdbc-simple-jdbc-parameters">
<title>Using SqlParameterSource to provide parameter values</title>
<para>Using 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 <classname>SqlParameterSource</classname>
interface that can be used instead. The first one we'll look at is
<classname>BeanPropertySqlParameterSource</classname> 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:</para>
<para><programlisting language="java"><![CDATA[public class JdbcActorDao implements ActorDao {
private SimpleJdbcTemplate simpleJdbcTemplate;
private SimpleJdbcInsert insertActor;
public void setDataSource(DataSource dataSource) {
this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource);
this.insertActor =
new SimpleJdbcInsert(dataSource)
.withTableName("t_actor")
.usingGeneratedKeyColumns("id");
}
public void add(Actor actor) {
SqlParameterSource parameters = new BeanPropertySqlParameterSource(actor);
Number newId = insertActor.executeAndReturnKey(parameters);
actor.setId(newId.longValue());
}
// ... additional methods
}]]></programlisting>Another option is the
<classname>MapSqlParameterSource</classname> that resembles a Map but
provides a more convenient <classname>addValue</classname> method that
can be chained.</para>
<para><programlisting language="java"><![CDATA[public class JdbcActorDao implements ActorDao {
private SimpleJdbcTemplate simpleJdbcTemplate;
private SimpleJdbcInsert insertActor;
public void setDataSource(DataSource dataSource) {
this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource);
this.insertActor =
new SimpleJdbcInsert(dataSource)
.withTableName("t_actor")
.usingGeneratedKeyColumns("id");
}
public void add(Actor actor) {
SqlParameterSource parameters = new MapSqlParameterSource()
.addValue("first_name", actor.getFirstName())
.addValue("last_name", actor.getLastName());
Number newId = insertActor.executeAndReturnKey(parameters);
actor.setId(newId.longValue());
}
// ... additional methods
}]]></programlisting>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.</para>
</section>
<section id="jdbc-simple-jdbc-call-1">
<title>Calling a stored procedure using SimpleJdbcCall</title>
<para>Let's now turn our attention to calling stored procedures using
the <classname>SimpleJdbcCall</classname> 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:</para>
<para><programlisting><![CDATA[CREATE PROCEDURE read_actor (
IN in_id INTEGER,
OUT out_first_name VARCHAR(100),
OUT out_last_name VARCHAR(100),
OUT out_birth_date DATE)
BEGIN
SELECT first_name, last_name, birth_date
INTO out_first_name, out_last_name, out_birth_date
FROM t_actor where id = in_id;
END;]]></programlisting>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.</para>
<para>The <classname>SimpleJdbcCall</classname> is declared in a similar
manner to the <classname>SimpleJdbcInsert</classname>, 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.</para>
<para><programlisting language="java"><![CDATA[public class JdbcActorDao implements ActorDao {
private SimpleJdbcTemplate simpleJdbcTemplate;
private SimpleJdbcCall procReadActor;
public void setDataSource(DataSource dataSource) {
this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource);
this.procReadActor =
new SimpleJdbcCall(dataSource)
.withProcedureName("read_actor");
}
public Actor readActor(Long id) {
SqlParameterSource in = new MapSqlParameterSource()
.addValue("in_id", id);
Map out = procReadActor.execute(in);
Actor actor = new Actor();
actor.setId(id);
actor.setFirstName((String) out.get("out_first_name"));
actor.setLastName((String) out.get("out_last_name"));
actor.setBirthDate((Date) out.get("out_birth_date"));
return actor;
}
// ... additional methods
}]]></programlisting>The execution of the call involves creating an
<classname>SqlParameterSource</classname> 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.</para>
<para>The <classname>execute</classname> 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
<classname>out_first_name, out_last_name</classname> and
<classname>out_birth_date</classname>.</para>
<para>The last part of the <classname>execute</classname> 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
<classname>CaseInsensitiveMap</classname> from the Jakarta Commons
project. The way you do that is by creating your own
<classname>JdbcTemplate</classname> and setting the
<classname>setResultsMapCaseInsensitive</classname> property to
<classname>true</classname>. Then you pass this customized
<classname>JdbcTemplate</classname> instance into the constructor of
your <classname>SimpleJdbcCall</classname>. You also have to include the
<classname>commons-collections.jar</classname> on your classpath for
this to work. Here is an example of this configuration:</para>
<para><programlisting language="java"><![CDATA[public class JdbcActorDao implements ActorDao {
private SimpleJdbcCall procReadActor;
public void setDataSource(DataSource dataSource) {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.setResultsMapCaseInsensitive(true);
this.procReadActor =
new SimpleJdbcCall(jdbcTemplate)
.withProcedureName("read_actor");
}
// ... additional methods
}]]></programlisting>By doing this, you don't have to worry about the case
used for the names of your returned out parameters.</para>
</section>
<section id="jdbc-simple-jdbc-call-2">
<title>Declaring parameters to use for a SimpleJdbcCall</title>
<para>We have seen how the parameters are deduced based on metadata, but
you can declare then explicitly if you wish. This is done when the
<classname>SimpleJdbcCall</classname> is created and configured using
the <classname>declareParameters</classname> method that takes a
variable number of <classname>SqlParameter</classname> objects as input.
See the next section for details on how to define an
<classname>SqlParameter</classname>.</para>
<para>We can opt to declare one, some or all of the parameters
explicitly. The parameter metadata is still being used. By calling the
method <classname>withoutProcedureColumnMetaDataAccess</classname> 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 <classname>useInParameterNames</classname> to specify
the list of in parameter names to include.</para>
<para>This is what a fully declared procedure call declaration of our
earlier example would look like:</para>
<para><programlisting language="java"><![CDATA[public class JdbcActorDao implements ActorDao {
private SimpleJdbcCall procReadActor;
public void setDataSource(DataSource dataSource) {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.setResultsMapCaseInsensitive(true);
this.procReadActor =
new SimpleJdbcCall(jdbcTemplate)
.withProcedureName("read_actor")
.withoutProcedureColumnMetaDataAccess()
.useInParameterNames("in_id")
.declareParameters(
new SqlParameter("in_id", Types.NUMERIC),
new SqlOutParameter("out_first_name", Types.VARCHAR),
new SqlOutParameter("out_last_name", Types.VARCHAR),
new SqlOutParameter("out_birth_date", Types.DATE)
);
}
// ... additional methods
}]]></programlisting>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.</para>
</section>
<section id="jdbc-params">
<title>How to define SqlParameters</title>
<para>To 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 <classname>SqlParameter</classname> or one of its subclasses.
You typically specify the parameter name and SQL type in the
constructor. The SQL type is specified using the
<classname>java.sql.Types</classname> constants. We have already seen
declarations like:</para>
<para><programlisting language="java"><![CDATA[ new SqlParameter("in_id", Types.NUMERIC),
new SqlOutParameter("out_first_name", Types.VARCHAR),]]></programlisting></para>
<para>The first line with the <classname>SqlParameter</classname>
declares an in parameter. In parameters can be used for both stored
procedure calls and for queries using the
<classname>SqlQuery</classname> and its subclasses covered in the
following section.</para>
<para>The second line with the <classname>SqlOutParameter</classname>
declares an out parameter to be used in a stored procedure call. There
is also an <classname>SqlInOutParameter</classname> for inout
parameters, parameters that provide an in value to the procedure and
that also return a value</para>
<note>
<para>Only parameters declared as <classname>SqlParameter</classname>
and <classname>SqlInOutParameter</classname> will be used to provide
input values. This is different from the
<classname>StoredProcedure</classname> class which for backwards
compatibility reasons allows input values to be provided for
parameters declared as <classname>SqlOutParameter</classname>.</para>
</note>
<para>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 <classname>RowMapper</classname> to handle
mapping of rows returned from a REF cursor. Another option is to specify
an <classname>SqlReturnType</classname> that provides and opportunity to
define customized handling of the return values.</para>
</section>
<section id="jdbc-simple-jdbc-call-3">
<title>Calling a stored function using SimpleJdbcCall</title>
<para>Calling 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 <classname>withFunctionName</classname> 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 <classname>executeFunction</classname> 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
<classname>executeObject</classname> is also available for stored
procedures that only have one out parameter. The following example is
based on a stored function named <classname>get_actor_name</classname>
that returns an actor's full name. Here is the MySQL source for this
function:</para>
<para><programlisting><![CDATA[CREATE FUNCTION get_actor_name (in_id INTEGER)
RETURNS VARCHAR(200) READS SQL DATA
BEGIN
DECLARE out_name VARCHAR(200);
SELECT concat(first_name, ' ', last_name)
INTO out_name
FROM t_actor where id = in_id;
RETURN out_name;
END;]]></programlisting></para>
<para>To call this function we again create a
<classname>SimpleJdbcCall</classname> in the initialization
method.</para>
<para><programlisting language="java"><![CDATA[public class JdbcActorDao implements ActorDao {
private SimpleJdbcTemplate simpleJdbcTemplate;
private SimpleJdbcCall funcGetActorName;
public void setDataSource(DataSource dataSource) {
this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource);
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.setResultsMapCaseInsensitive(true);
this.funcGetActorName =
new SimpleJdbcCall(jdbcTemplate)
.withFunctionName("get_actor_name");
}
public String getActorName(Long id) {
SqlParameterSource in = new MapSqlParameterSource()
.addValue("in_id", id);
String name = funcGetActorName.executeFunction(String.class, in);
return name;
}
// ... additional methods
}]]></programlisting>The execute method used returns a
<classname>String</classname> containing the return value from the
function call.</para>
</section>
<section id="jdbc-simple-jdbc-call-4">
<title>Returning ResultSet/REF Cursor from a SimpleJdbcCall</title>
<para>Calling 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 <classname>SimpleJdbcCall</classname> you use
the <classname>returningResultSet</classname> method and declare a
<classname>RowMapper</classname> 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
<classname>RowMapper</classname> implementations. The name specified
will still be used to store the processed list of results in the results
map returned from the execute statement.</para>
<para>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:</para>
<para><programlisting><![CDATA[CREATE PROCEDURE read_all_actors()
BEGIN
SELECT a.id, a.first_name, a.last_name, a.birth_date FROM t_actor a;
END;]]></programlisting>In order to call this procedure we need to declare the
<classname>RowMapper</classname> to be used. Since the class we want to
map to follows the JavaBean rules, we can use a
<classname>ParameterizedBeanPropertyRowMapper</classname> that is
created by passing in the required class to map to in the
<classname>newInstance</classname> method.</para>
<para><programlisting language="java"><![CDATA[public class JdbcActorDao implements ActorDao {
private SimpleJdbcTemplate simpleJdbcTemplate;
private SimpleJdbcCall procReadAllActors;
public void setDataSource(DataSource dataSource) {
this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource);
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.setResultsMapCaseInsensitive(true);
this.procReadAllActors =
new SimpleJdbcCall(jdbcTemplate)
.withProcedureName("read_all_actors")
.returningResultSet("actors",
ParameterizedBeanPropertyRowMapper.newInstance(Actor.class));
}
public List getActorsList() {
Map m = procReadAllActors.execute(new HashMap<String, Object>(0));
return (List) m.get("actors");
}
// ... additional methods
}]]></programlisting>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.</para>
</section>
</section>
<section id="jdbc-object">
<title>Modeling JDBC operations as Java objects</title>
<para>The <literal>org.springframework.jdbc.object</literal> 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.</para>
<note>
<para>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 <link
linkend="jdbc-StoredProcedure"><classname>StoredProcedure</classname></link>
class) can often be replaced with straight
<classname>JdbcTemplate</classname> calls... often it is simpler to use
and plain easier to read a DAO method that simply calls a method on a
<classname>JdbcTemplate</classname> direct (as opposed to encapsulating
a query as a full-blown class).</para>
<para>It must be stressed however that this is just a
<emphasis>view</emphasis>... if you feel that you are getting measurable
value from using the RDBMS operation classes, feel free to continue
using these classes.</para>
</note>
<section id="jdbc-SqlQuery">
<title><classname>SqlQuery</classname></title>
<para><classname>SqlQuery</classname> is a reusable, threadsafe class
that encapsulates an SQL query. Subclasses must implement the
<methodname>newRowMapper(..)</methodname> method to provide a
<interfacename>RowMapper</interfacename> instance that can create one
object per row obtained from iterating over the
<interfacename>ResultSet</interfacename> that is created during the
execution of the query. The <classname>SqlQuery</classname> class is
rarely used directly since the <classname>MappingSqlQuery</classname>
subclass provides a much more convenient implementation for mapping rows
to Java classes. Other implementations that extend
<classname>SqlQuery</classname> are
<classname>MappingSqlQueryWithParameters</classname> and
<classname>UpdatableSqlQuery</classname>.</para>
</section>
<section id="jdbc-MappingSqlQuery">
<title><classname>MappingSqlQuery</classname></title>
<para><classname>MappingSqlQuery</classname> is a reusable query in
which concrete subclasses must implement the abstract
<methodname>mapRow(..)</methodname> method to convert each row of the
supplied <interfacename>ResultSet</interfacename> 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
<classname>Actor</classname> class.</para>
<programlisting language="java"><![CDATA[public class ActorMappingQuery extends MappingSqlQuery<Actor> {
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;
}
}]]></programlisting>
<para>The class extends <classname>MappingSqlQuery</classname>
parameterized with the <classname>Actor</classname> type. We provide a
constructor for this customer query that takes the
<interfacename>DataSource</interfacename> as the only parameter. In this
constructor we call the constructor on the superclass with the
<interfacename>DataSource</interfacename> and the SQL that should be
executed to retrieve the rows for this query. This SQL will be used to
create a <interfacename>PreparedStatement</interfacename> so it may
contain place holders for any parameters to be passed in during
execution. Each parameter must be declared using the
<literal>declareParameter</literal> method passing in an
<classname>SqlParameter</classname>. The
<classname>SqlParameter</classname> takes a name and the JDBC type as
defined in <classname>java.sql.Types</classname>. After all parameters
have been defined we call the <literal>compile()</literal> 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.</para>
<programlisting language="java"><![CDATA[private ActorMappingQuery actorMappingQuery;
@Autowired
public void setDataSource(DataSource dataSource) {
this.actorMappingQuery = new ActorMappingQuery(dataSource);
}
public Customer getCustomer(Long id) {
return actorMappingQuery.findObject(id);
}]]></programlisting>
<para>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.</para>
<programlisting language="java"><![CDATA[public List<Actor> searchForActors(int age, String namePattern) {
List<Actor> actors = actorSearchMappingQuery.execute(age, namePattern);
return actors;
}]]></programlisting>
</section>
<section id="jdbc-SqlUpdate">
<title><classname>SqlUpdate</classname></title>
<para>The <classname>SqlUpdate</classname> class encapsulates an SQL
update. Like a query, an update object is reusable, and like all
<classname>RdbmsOperation</classname> classes, an update can have
parameters and is defined in SQL. This class provides a number of
<methodname>update(..)</methodname> methods analogous to the
<methodname>execute(..)</methodname> 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.</para>
<programlisting language="java"><![CDATA[import java.sql.Types;
import javax.sql.DataSource;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.object.SqlUpdate;
public class UpdateCreditRating extends SqlUpdate {
public UpdateCreditRating(DataSource ds) {
setDataSource(ds);
setSql("update customer set credit_rating = ? where id = ?");
declareParameter(new SqlParameter("creditRating", Types.NUMERIC));
declareParameter(new SqlParameter("id", Types.NUMERIC));
compile();
}
/**
* @param id for the Customer to be updated
* @param rating the new value for credit rating
* @return number of rows updated
*/
public int execute(int id, int rating) {
return update(rating, id);
}
}]]></programlisting>
</section>
<section id="jdbc-StoredProcedure">
<title><classname>StoredProcedure</classname></title>
<para>The <classname>StoredProcedure</classname> class is a superclass
for object abstractions of RDBMS stored procedures. This class is
<literal>abstract</literal>, and its various
<literal>execute(..)</literal> methods have <literal>protected</literal>
access, preventing use other than through a subclass that offers tighter
typing.</para>
<para>The inherited <literal>sql</literal> property will be the name of
the stored procedure in the RDBMS.</para>
<para>To define a parameter to be used for the StoredProcedure class,
you use an <classname>SqlParameter</classname> or one of its subclasses.
You must specify the parameter name and SQL type in the constructor. The
SQL type is specified using the <classname>java.sql.Types</classname>
constants. We have already seen declarations like:</para>
<para><programlisting language="java"><![CDATA[ new SqlParameter("in_id", Types.NUMERIC),
new SqlOutParameter("out_first_name", Types.VARCHAR),]]></programlisting></para>
<para>The first line with the <classname>SqlParameter</classname>
declares an in parameter. In parameters can be used for both stored
procedure calls and for queries using the
<classname>SqlQuery</classname> and its subclasses covered in the
following section.</para>
<para>The second line with the <classname>SqlOutParameter</classname>
declares an out parameter to be used in the stored procedure call. There
is also an <classname>SqlInOutParameter</classname> for inout
parameters, parameters that provide an in value to the procedure and
that also return a value</para>
<para><note>
<para>Parameters declared as <classname>SqlParameter</classname> and
<classname>SqlInOutParameter</classname> will always be used to
provide input values. In addition to this any parameter declared as
<classname>SqlOutParameter</classname> where an non-null input value
is provided will also be used as an input parameter.</para>
</note></para>
<para>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 <classname>RowMapper</classname> to handle
mapping of rows returned from a REF cursor. Another option is to specify
an <classname>SqlReturnType</classname> that provides and opportunity to
define customized handling of the return values.</para>
<para>Here is an example of a simple DAO that uses a
<classname>StoredProcedure</classname> to call a function,
<literal>sysdate()</literal>, that comes with any Oracle database. To
use the stored procedure functionality you have to create a class that
extends <classname>StoredProcedure</classname>. In this example the
<classname>StoredProcedure</classname> class is an inner class, but if
you need to reuse the <classname>StoredProcedure</classname> 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 <classname>SqlOutParameter</classname>. The
<literal>execute()</literal> method executes the procedure and extracts
the returned date from the results <classname>Map</classname>. The
results <classname>Map</classname> has an entry for each declared output
parameter, in this case only one, using the parameter name as the
key.</para>
<programlisting language="java"><![CDATA[import java.sql.Types;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.SqlOutParameter;
import org.springframework.jdbc.object.StoredProcedure;
public class StoredProcedureDao {
private GetSysdateProcedure getSysdate;
@Autowired
public void init(DataSource dataSource) {
this.getSysdate = new GetSysdateProcedure(dataSource);
}
public Date getSysdate() {
return getSysdate.execute();
}
private class GetSysdateProcedure extends StoredProcedure {
private static final String SQL = "sysdate";
public GetSysdateProcedure(DataSource dataSource) {
setDataSource(dataSource);
setFunction(true);
setSql(SQL);
declareParameter(new SqlOutParameter("date", Types.DATE));
compile();
}
public Date execute() {
// the 'sysdate' sproc has no input parameters, so an empty Map is supplied...
Map<String, Object> results = execute(new HashMap<String, Object>());
Date sysdate = (Date) results.get("date");
return sysdate;
}
}
}]]></programlisting>
<para>Below is an example of a <classname>StoredProcedure</classname>
that has two output parameters (in this case Oracle REF cursors).</para>
<programlisting language="java"><![CDATA[import oracle.jdbc.OracleTypes;
import org.springframework.jdbc.core.SqlOutParameter;
import org.springframework.jdbc.object.StoredProcedure;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
public class TitlesAndGenresStoredProcedure extends StoredProcedure {
private static final String SPROC_NAME = "AllTitlesAndGenres";
public TitlesAndGenresStoredProcedure(DataSource dataSource) {
super(dataSource, SPROC_NAME);
declareParameter(new SqlOutParameter("titles", OracleTypes.CURSOR, new TitleMapper()));
declareParameter(new SqlOutParameter("genres", OracleTypes.CURSOR, new GenreMapper()));
compile();
}
public Map<String, Object> execute() {
// again, this sproc has no input parameters, so an empty Map is supplied
return super.execute(new HashMap<String, Object>());
}
}]]></programlisting>
<para>Notice how the overloaded variants of the
<literal>declareParameter(..)</literal> method that have been used in
the <classname>TitlesAndGenresStoredProcedure</classname> constructor
are passed <interfacename>RowMapper</interfacename> implementation
instances; this is a very convenient and powerful way to reuse existing
functionality. (The code for the two
<interfacename>RowMapper</interfacename> implementations is provided
below in the interest of completeness.)</para>
<para>First the <classname>TitleMapper</classname> class, which simply
maps a <interfacename>ResultSet</interfacename> to a
<classname>Title</classname> domain object for each row in the supplied
<interfacename>ResultSet</interfacename>.</para>
<programlisting language="java"><![CDATA[import org.springframework.jdbc.core.RowMapper;
import java.sql.ResultSet;
import java.sql.SQLException;
import com.foo.domain.Title;
public final class TitleMapper implements RowMapper<Title> {
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;
}
}]]></programlisting>
<para>Second, the <classname>GenreMapper</classname> class, which again
simply maps a <interfacename>ResultSet</interfacename> to a
<classname>Genre</classname> domain object for each row in the supplied
<interfacename>ResultSet</interfacename>.</para>
<programlisting language="java"><![CDATA[import org.springframework.jdbc.core.RowMapper;
import java.sql.ResultSet;
import java.sql.SQLException;
import com.foo.domain.Genre;
public final class GenreMapper implements RowMapper<Genre> {
public Genre mapRow(ResultSet rs, int rowNum) throws SQLException {
return new Genre(rs.getString("name"));
}
}]]></programlisting>
<para>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 <literal>execute(..)</literal> method which would delegate to the
superclass' (untyped) <literal>execute(Map parameters)</literal> (which
has <literal>protected</literal> access); for example:</para>
<programlisting language="java"><![CDATA[import oracle.jdbc.OracleTypes;
import org.springframework.jdbc.core.SqlOutParameter;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.object.StoredProcedure;
import javax.sql.DataSource;
import java.sql.Types;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class TitlesAfterDateStoredProcedure extends StoredProcedure {
private static final String SPROC_NAME = "TitlesAfterDate";
private static final String CUTOFF_DATE_PARAM = "cutoffDate";
public TitlesAfterDateStoredProcedure(DataSource dataSource) {
super(dataSource, SPROC_NAME);
declareParameter(new SqlParameter(CUTOFF_DATE_PARAM, Types.DATE);
declareParameter(new SqlOutParameter("titles", OracleTypes.CURSOR, new TitleMapper()));
compile();
}
public Map<String, Object> execute(Date cutoffDate) {
Map<String, Object> inputs = new HashMap<String, Object>();
inputs.put(CUTOFF_DATE_PARAM, cutoffDate);
return super.execute(inputs);
}
}]]></programlisting>
</section>
</section>
<section id="jdbc-parameter-handling">
<title>Common issues with parameter and data value handling</title>
<para>There are some issues involving parameters and data values that are
common across all the different approaches provided by the Spring JDBC
Framework.</para>
<section id="jdbc-type-information">
<title>Providing SQL type information for parameters</title>
<para>Most 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.</para>
<para>There are a few different ways this can be accomplished:</para>
<itemizedlist>
<listitem>
<para>Many of the update and query methods of the
<classname>JdbcTemplate</classname> take an additional parameter in
the form of an int array. This array should contain the SQL type
using constant values from the <classname>java.sql.Types</classname>
class. There must be one entry for each parameter.</para>
</listitem>
</itemizedlist>
<itemizedlist>
<listitem>
<para>You can wrap the parameter value that needs this additional
information using the <classname>SqlParameterValue</classname>
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.</para>
</listitem>
</itemizedlist>
<itemizedlist>
<listitem>
<para>For methods working with named parameters, you can use the
<classname>SqlParameterSource</classname> classes
<classname>BeanPropertySqlParameterSource</classname> or
<classname>MapSqlParameterSource</classname>. They both have methods
for registering the SQL type for any of the named parameter
values.</para>
</listitem>
</itemizedlist>
</section>
<section id="jdbc-lob">
<title>Handling BLOB and CLOB objects</title>
<para>You 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
<classname>LobHandler</classname> interface for the actual management of
the LOB data. The <classname>LobHandler</classname> provides access to a
<classname>LobCreator</classname>, via the
<classname>getLobCreator</classname> method, for creating new LOB
objects to be inserted.</para>
<para>The <classname>LobCreator/LobHandler</classname> provides the
following support for LOB in- and output:</para>
<para><itemizedlist>
<listitem>
<para>BLOB</para>
<itemizedlist>
<listitem>
<para>byte[] getBlobAsBytes and setBlobAsBytes</para>
</listitem>
<listitem>
<para>InputStream getBlobAsBinaryStream and
setBlobAsBinaryStream</para>
</listitem>
</itemizedlist>
</listitem>
</itemizedlist><itemizedlist>
<listitem>
<para>CLOB</para>
<itemizedlist>
<listitem>
<para>String getClobAsString and setClobAsString</para>
</listitem>
<listitem>
<para>InputStream getClobAsAsciiStream and
setClobAsAsciiStream</para>
</listitem>
<listitem>
<para>Reader getClobAsCharacterStream and
setClobAsCharacterStream</para>
</listitem>
</itemizedlist>
</listitem>
</itemizedlist></para>
<para>We 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.</para>
<para>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.</para>
<para>We are assuming that we have a variable named 'lobHandler' that
already is set to an instance of a
<classname>DefaultLobHandler</classname>. This is typically done using
dependency injection.</para>
<programlistingco>
<areaspec>
<area coords="8" id="jdbc.lobhandler.variableref" />
<area coords="12" id="jdbc.lobhandler.setClob" />
<area coords="13" id="jdbc.lobhandler.setBlob" />
</areaspec>
<programlisting language="java"><![CDATA[final File blobIn = new File("spring2004.jpg");
final InputStream blobIs = new FileInputStream(blobIn);
final File clobIn = new File("large.txt");
final InputStream clobIs = new FileInputStream(clobIn);
final InputStreamReader clobReader = new InputStreamReader(clobIs);
jdbcTemplate.execute(
"INSERT INTO lob_table (id, a_clob, a_blob) VALUES (?, ?, ?)",
new AbstractLobCreatingPreparedStatementCallback(lobHandler) {
protected void setValues(PreparedStatement ps, LobCreator lobCreator)
throws SQLException {
ps.setLong(1, 1L);
lobCreator.setClobAsCharacterStream(ps, 2, clobReader, (int)clobIn.length());
lobCreator.setBlobAsBinaryStream(ps, 3, blobIs, (int)blobIn.length());
}
}
);
blobIs.close();
clobReader.close();]]></programlisting>
<calloutlist>
<callout arearefs="jdbc.lobhandler.variableref">
<para>Here we use the lobHandler that in this example is a plain
<classname>DefaultLobHandler</classname></para>
</callout>
<callout arearefs="jdbc.lobhandler.setClob">
<para>Using the method <classname>setClobAsCharacterStream
</classname>we pass in the contents of the CLOB</para>
</callout>
<callout arearefs="jdbc.lobhandler.setBlob">
<para>Using the method
<classname>setBlobAsBinartStream</classname> we pass in the
contents of the BLOB</para>
</callout>
</calloutlist>
</programlistingco>
<para>Now 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 <classname>DefaultLobHandler</classname>.</para>
<para><programlistingco>
<areaspec>
<area coords="5" id="jdbc.lobhandler.getClob" />
<area coords="7" id="jdbc.lobhandler.getBlob" />
</areaspec>
<programlisting language="java"><![CDATA[List<Map<String, Object>> l = jdbcTemplate.query("select id, a_clob, a_blob from lob_table",
new RowMapper<Map<String, Object>>() {
public Map<String, Object> mapRow(ResultSet rs, int i) throws SQLException {
Map<String, Object> results = new HashMap<String, Object>();
String clobText = lobHandler.getClobAsString(rs, "a_clob");
results.put("CLOB", clobText);
byte[] blobBytes = lobHandler.getBlobAsBytes(rs, "a_blob");
results.put("BLOB", blobBytes);
return results;
}
});
]]></programlisting>
<calloutlist>
<callout arearefs="jdbc.lobhandler.setClob">
<para>Using the method <classname>getClobAsString </classname>we
retrieve the contents of the CLOB</para>
</callout>
<callout arearefs="jdbc.lobhandler.setBlob">
<para>Using the method <classname>getBlobAsBytes</classname> we
retrieve the contents of the BLOB</para>
</callout>
</calloutlist>
</programlistingco></para>
</section>
<section id="jdbc-in-clause">
<title>Passing in lists of values for IN clause</title>
<para>The 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
<classname>NamedParameterJdbcTemplate</classname> and
<classname>SimpleJdbcTemplate</classname> takes the latter approach.
When you pass in the values you should pass them in as a
<classname>java.util.List</classname> of primitive objects. This list
will be used to insert the required place holders and pass in the values
during the statement execution.</para>
<note>
<para>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.</para>
</note>
<para>In addition to the primitive values in the value list, you can
create a <classname>java.util.List</classname> 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.</para>
</section>
<section id="jdbc-complex-types">
<title>Handling complex types for stored procedure calls</title>
<para>When calling stored procedures it's sometimes possible to use
complex types specific to the database. To accommodate these types
Spring provides a <classname>SqlReturnType</classname> for handling them
when they are returned from the stored procedure call and
<classname>SqlTypeValue</classname> when they are passed in as a
parameter to the stored procedure.</para>
<para>Here is an example of returning the value of an Oracle STRUCT
object of the user declared type "ITEM_TYPE". The
<classname>SqlReturnType</classname> interface has a single method named
"<classname>getTypeValue</classname>" that must be implemented. This
interface is used as part of the declaration of an
<classname>SqlOutParameter</classname>.</para>
<para><programlisting language="java"><![CDATA[final TestItem - new TestItem(123L, "A test item",
new SimpleDateFormat("yyyy-M-d").parse("2010-12-31"););
declareParameter(new SqlOutParameter("item", OracleTypes.STRUCT, "ITEM_TYPE",
new SqlReturnType() {
public Object getTypeValue(CallableStatement cs, int colIndx, int sqlType, String typeName)
throws SQLException {
STRUCT struct = (STRUCT)cs.getObject(colIndx);
Object[] attr = struct.getAttributes();
TestItem item = new TestItem();
item.setId(((Number) attr[0]).longValue());
item.setDescription((String)attr[1]);
item.setExpirationDate((java.util.Date)attr[2]);
return item;
}
}));]]></programlisting>Going from Java to the database and passing in the
value of a <classname>TestItem</classname> into a stored procedure is
done using the <classname>SqlTypeValue</classname>. The
<classname>SqlTypeValue</classname> interface has a single method named
"<classname>createTypeValue</classname>" that must be implemented. The
active connection is passed in and can be used to create database
specific objects like <classname>StructDescriptor</classname>s or
<classname>ArrayDescriptor</classname>s</para>
<para><programlisting language="java"><![CDATA[final TestItem - new TestItem(123L, "A test item",
new SimpleDateFormat("yyyy-M-d").parse("2010-12-31"););
SqlTypeValue value = new AbstractSqlTypeValue() {
protected Object createTypeValue(Connection conn, int sqlType, String typeName) throws SQLException {
StructDescriptor itemDescriptor = new StructDescriptor(typeName, conn);
Struct item = new STRUCT(itemDescriptor, conn,
new Object[] {
testItem.getId(),
testItem.getDescription(),
new java.sql.Date(testItem.getExpirationDate().getTime())
});
return item;
}
};]]></programlisting>This <classname>SqlTypeValue</classname> can now be
added to the Map containing the input parameters for the execute call of
the stored procedure.</para>
<para>Another use for the <classname>SqlTypeValue</classname> is for
passing in an array of values to an Oracle stored procedure. Oracle has
its own internal <classname>ARRAY</classname> class that must be used in
this case and we can use the <classname>SqlTypeValue</classname> to
create an instance of the Oracle <classname>ARRAY</classname> and
populate it with values from our Java array.</para>
<programlisting language="java"><![CDATA[final Long[] ids = new Long[] {1L, 2L};
SqlTypeValue value = new AbstractSqlTypeValue() {
protected Object createTypeValue(Connection conn, int sqlType, String typeName) throws SQLException {
ArrayDescriptor arrayDescriptor = new ArrayDescriptor(typeName, conn);
ARRAY idArray = new ARRAY(arrayDescriptor, conn, ids);
return idArray;
}
};]]></programlisting>
</section>
</section>
<section id="jdbc-embedded-database-support">
<title>Embedded database support</title>
<para>The <literal>org.springframework.jdbc.datasource.embedded</literal>
package provides support for embedded Java database engines. Support for
<ulink url="http://www.hsqldb.org">HSQL</ulink> and <ulink
url="http://www.h2database.com">H2</ulink> is provided natively. There is
also an extensible API for plugging in new embedded database types and
<classname>DataSource</classname> implementations.</para>
<section id="jdbc-why-embedded-database">
<title>Why use a embedded database?</title>
<para>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.</para>
</section>
<section id="jdbc-embedded-database-xml">
<title>Creating an embedded database instance using Spring XML</title>
<para>When 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: <programlisting language="xml"><![CDATA[ <jdbc:embedded-database id="dataSource">
<jdbc:script location="classpath:schema.sql"/>
<jdbc:script location="classpath:test-data.sql"/>
</jdbc:embedded-database>
]]></programlisting></para>
<para>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 <classname>javax.sql.DataSource</classname>.
This bean can then be injected into data access objects as
needed.</para>
</section>
<section id="jdbc-embedded-database-java">
<title>Creating an embedded database instance programatically</title>
<para>The <classname>EmbeddedDatabaseBuilder</classname> 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:
<programlisting language="java"><![CDATA[ EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
EmbeddedDatabase db = builder.type(H2).script("schema.sql").script("test-data.sql").build();
// do stuff against the db (EmbeddedDatabase extends javax.sql.DataSource)
db.shutdown()
]]></programlisting></para>
</section>
<section id="jdbc-embedded-database-extension">
<title>Extending the embedded database support</title>
<para>Spring Jdbc's embedded database support can be extended in two
ways: <orderedlist>
<listitem>
<para>Implement <classname>EmbeddedDatabaseConfigurer</classname>
to support a new embedded database type, such as Apache
Derby.</para>
</listitem>
<listitem>
<para>Implement <classname>DataSourceFactory</classname> to
support a new DataSource implementation, such as a connection
pool, to manage embedded database connections.</para>
</listitem>
</orderedlist></para>
<para>You are encouraged to contribute back extensions to the Spring
community at <ulink
url="jira.springframework.org">jira.springframework.org</ulink>.</para>
</section>
</section>
</chapter>