2704 lines
124 KiB
XML
2704 lines
124 KiB
XML
<?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<Actor> mapper = new RowMapper<Actor>() {
|
||
public Actor mapRow(ResultSet rs, int rowNum) throws SQLException {
|
||
Actor actor = new Actor();
|
||
actor.setId(rs.getLong("id"));
|
||
actor.setFirstName(rs.getString("first_name"));
|
||
actor.setLastName(rs.getString("last_name"));
|
||
return actor;
|
||
}
|
||
};
|
||
|
||
|
||
<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<Actor> mapper = new RowMapper<Actor>() {
|
||
public Actor mapRow(ResultSet rs, int rowNum) throws SQLException {
|
||
Actor actor = new Actor();
|
||
actor.setId(rs.getLong("id"));
|
||
actor.setFirstName(rs.getString("first_name"));
|
||
actor.setLastName(rs.getString("last_name"));
|
||
return actor;
|
||
}
|
||
};
|
||
|
||
<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> |