updating code examples with generics/varargs; polishing

This commit is contained in:
Thomas Risberg 2009-05-12 18:18:29 +00:00
parent c55569b51e
commit 95213d488c
1 changed files with 327 additions and 192 deletions

View File

@ -88,9 +88,10 @@
<listitem> <listitem>
<para><emphasis role="bold">SimpleJdbcTemplate</emphasis> - this <para><emphasis role="bold">SimpleJdbcTemplate</emphasis> - this
class combines the most frequently used operations across class combines the most frequently used operations across
JdbcTemplate and NamedParameterJdbcTemplate. JdbcTemplate and NamedParameterJdbcTemplate. It also adds some
It also adds some additional convenience around support for Java 5 varargs additional convenience around support for Java 5 varargs where this
where this was not possible in the JdbcTemplate due to backwards compatibility reasons.</para> was not possible in the JdbcTemplate due to backwards compatibility
reasons.</para>
</listitem> </listitem>
<listitem> <listitem>
@ -141,9 +142,11 @@
contains a utility class for easy contains a utility class for easy
<interfacename>DataSource</interfacename> access, and various simple <interfacename>DataSource</interfacename> access, and various simple
<interfacename>DataSource</interfacename> implementations that can be <interfacename>DataSource</interfacename> implementations that can be
used for testing and running unmodified JDBC code outside of a Java EE container. used for testing and running unmodified JDBC code outside of a Java EE
A sub-package named <literal>org.springfamework.jdbc.datasource.embedded</literal> container. A sub-package named
provides support for creating in-memory database instances using Java database engines such as HSQL and H2.</para> <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> <para>Next, the <literal>org.springframework.jdbc.object</literal>
package contains classes that represent RDBMS queries, updates, and package contains classes that represent RDBMS queries, updates, and
@ -238,21 +241,21 @@
<para>Querying for a <classname>String</classname>.</para> <para>Querying for a <classname>String</classname>.</para>
<programlisting language="java"><![CDATA[String surname = this.jdbcTemplate.queryForObject( <programlisting language="java"><![CDATA[String lastName = this.jdbcTemplate.queryForObject(
"select surname from t_actor where id = ?", "select last_name from t_actor where id = ?",
new Object[]{1212L}, String.class);]]></programlisting> new Object[]{1212L}, String.class);]]></programlisting>
<para>Querying and populating a <emphasis>single</emphasis> domain <para>Querying and populating a <emphasis>single</emphasis> domain
object.</para> object.</para>
<programlisting language="java"><![CDATA[Actor actor = this.jdbcTemplate.queryForObject( <programlisting language="java"><![CDATA[Actor actor = this.jdbcTemplate.queryForObject(
"select first_name, surname from t_actor where id = ?", "select first_name, last_name from t_actor where id = ?",
new Object[]{1212L}, new Object[]{1212L},
new RowMapper<Actor>() { new RowMapper<Actor>() {
public Actor mapRow(ResultSet rs, int rowNum) throws SQLException { public Actor mapRow(ResultSet rs, int rowNum) throws SQLException {
Actor actor = new Actor(); Actor actor = new Actor();
actor.setFirstName(rs.getString("first_name")); actor.setFirstName(rs.getString("first_name"));
actor.setSurname(rs.getString("surname")); actor.setLastName(rs.getString("last_name"));
return actor; return actor;
} }
}); });
@ -261,12 +264,12 @@
<para>Querying and populating a number of domain objects.</para> <para>Querying and populating a number of domain objects.</para>
<programlisting language="java"><![CDATA[List<Actor> actors = this.jdbcTemplate.query( <programlisting language="java"><![CDATA[List<Actor> actors = this.jdbcTemplate.query(
"select first_name, surname from t_actor", "select first_name, last_name from t_actor",
new RowMapper<Actor>() { new RowMapper<Actor>() {
public Actor mapRow(ResultSet rs, int rowNum) throws SQLException { public Actor mapRow(ResultSet rs, int rowNum) throws SQLException {
Actor actor = new Actor(); Actor actor = new Actor();
actor.setFirstName(rs.getString("first_name")); actor.setFirstName(rs.getString("first_name"));
actor.setSurname(rs.getString("surname")); actor.setLastName(rs.getString("last_name"));
return actor; return actor;
} }
}); });
@ -281,7 +284,7 @@
be better off written like so:</para> be better off written like so:</para>
<programlisting language="java"><![CDATA[public List<Actor> findAllActors() { <programlisting language="java"><![CDATA[public List<Actor> findAllActors() {
return this.jdbcTemplate.query( "select first_name, surname from t_actor", new ActorMapper()); return this.jdbcTemplate.query( "select first_name, last_name from t_actor", new ActorMapper());
} }
private static final class ActorMapper implements RowMapper<Actor> { private static final class ActorMapper implements RowMapper<Actor> {
@ -289,7 +292,7 @@ private static final class ActorMapper implements RowMapper<Actor> {
public Actor mapRow(ResultSet rs, int rowNum) throws SQLException { public Actor mapRow(ResultSet rs, int rowNum) throws SQLException {
Actor actor = new Actor(); Actor actor = new Actor();
actor.setFirstName(rs.getString("first_name")); actor.setFirstName(rs.getString("first_name"));
actor.setSurname(rs.getString("surname")); actor.setLastName(rs.getString("last_name"));
return actor; return actor;
} }
}]]></programlisting> }]]></programlisting>
@ -299,16 +302,16 @@ private static final class ActorMapper implements RowMapper<Actor> {
<title>Updating (INSERT/UPDATE/DELETE)</title> <title>Updating (INSERT/UPDATE/DELETE)</title>
<programlisting language="java"><![CDATA[this.jdbcTemplate.update( <programlisting language="java"><![CDATA[this.jdbcTemplate.update(
"insert into t_actor (first_name, surname) values (?, ?)", "insert into t_actor (first_name, last_name) values (?, ?)",
new Object[] {"Leonor", "Watling"});]]></programlisting> "Leonor", "Watling");]]></programlisting>
<programlisting language="java"><![CDATA[this.jdbcTemplate.update( <programlisting language="java"><![CDATA[this.jdbcTemplate.update(
"update t_actor set weapon = ? where id = ?", "update t_actor set = ? where id = ?",
new Object[] {"Banjo", new Long(5276)});]]></programlisting> "Banjo", 5276L);]]></programlisting>
<programlisting language="java"><![CDATA[this.jdbcTemplate.update( <programlisting language="java"><![CDATA[this.jdbcTemplate.update(
"delete from actor where id = ?", "delete from actor where id = ?",
new Object[] {new Long.valueOf(actorId)});]]></programlisting> Long.valueOf(actorId));]]></programlisting>
</section> </section>
<section id="jdbc-JdbcTemplate-examples-other"> <section id="jdbc-JdbcTemplate-examples-other">
@ -327,7 +330,7 @@ private static final class ActorMapper implements RowMapper<Actor> {
<programlisting language="java"><![CDATA[this.jdbcTemplate.update( <programlisting language="java"><![CDATA[this.jdbcTemplate.update(
"call SUPPORT.REFRESH_ACTORS_SUMMARY(?)", "call SUPPORT.REFRESH_ACTORS_SUMMARY(?)",
new Object[]{Long.valueOf(unionId)});]]></programlisting> Long.valueOf(unionId));]]></programlisting>
</section> </section>
</section> </section>
@ -368,37 +371,85 @@ private static final class ActorMapper implements RowMapper<Actor> {
<lineannotation>// JDBC-backed implementations of the methods on the <interfacename>CorporateEventDao</interfacename> follow...</lineannotation> <lineannotation>// JDBC-backed implementations of the methods on the <interfacename>CorporateEventDao</interfacename> follow...</lineannotation>
}</programlisting> }</programlisting>
<para>The attendant configuration might look like this.</para> <para>The corresponding configuration might look like this.</para>
<programlisting language="xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt; <programlisting language="xml"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
&lt;beans xmlns="http://www.springframework.org/schema/beans" <beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"&gt; 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">
&lt;bean id="corporateEventDao" class="com.example.JdbcCorporateEventDao"&gt; <bean id="corporateEventDao" class="com.example.JdbcCorporateEventDao">
&lt;property name="dataSource" ref="dataSource"/&gt; <property name="dataSource" ref="dataSource"/>
&lt;/bean&gt; </bean>
<lineannotation>&lt;!-- the <interfacename>DataSource</interfacename> (parameterized for configuration via a <link <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
linkend="beans-factory-placeholderconfigurer"><classname>PropertyPlaceHolderConfigurer</classname></link>) --&gt;</lineannotation> <property name="driverClassName" value="${jdbc.driverClassName}"/>
&lt;bean id="dataSource" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource"&gt; <property name="url" value="${jdbc.url}"/>
&lt;property name="driverClassName" value="${jdbc.driverClassName}"/&gt; <property name="username" value="${jdbc.username}"/>
&lt;property name="url" value="${jdbc.url}"/&gt; <property name="password" value="${jdbc.password}"/>
&lt;property name="username" value="${jdbc.username}"/&gt; </bean>
&lt;property name="password" value="${jdbc.password}"/&gt;
&lt;/bean&gt;
&lt;/beans&gt;</programlisting> <context:property-placeholder location="jdbc.properties"/>
<para>If you are using Spring's <classname>JdbcDaoSupport</classname> </beans>]]></programlisting>
class, and your various JDBC-backed DAO classes extend from it, then
you inherit a <methodname>setDataSource(..)</methodname> method for <para>An alternative to explicit configuration is to use the component
free from said superclass. It is totally up to you as to whether or scanning and annotation support for dependency injection. In this case
not you inherit from said class, you certainly are not forced to. If we woul annotate the setter method for the
you look at the source for the <classname>JdbcDaoSupport</classname> <classname>DataSource</classname> with the
class you will see that there is not a whole lot to it... it is <interfacename>@Autowired</interfacename> annotation.</para>
provided as a convenience only.</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 <para>Regardless of which of the above template initialization styles
you choose to use (or not), there is (almost) certainly no need to you choose to use (or not), there is (almost) certainly no need to
@ -438,7 +489,7 @@ public void setDataSource(DataSource dataSource) {
public int countOfActorsByFirstName(String firstName) { public int countOfActorsByFirstName(String firstName) {
String sql = "select count(0) from T_ACTOR where first_name = :first_name"; String sql = "select count(*) from T_ACTOR where first_name = :first_name";
SqlParameterSource namedParameters = new MapSqlParameterSource("first_name", firstName); SqlParameterSource namedParameters = new MapSqlParameterSource("first_name", firstName);
@ -468,7 +519,7 @@ public void setDataSource(DataSource dataSource) {
public int countOfActorsByFirstName(String firstName) { public int countOfActorsByFirstName(String firstName) {
String sql = "select count(0) from T_ACTOR where first_name = :first_name"; String sql = "select count(*) from T_ACTOR where first_name = :first_name";
Map namedParameters = Collections.singletonMap("first_name", firstName); Map namedParameters = Collections.singletonMap("first_name", firstName);
@ -531,7 +582,8 @@ public void setDataSource(DataSource dataSource) {
public int countOfActors(Actor exampleActor) { public int countOfActors(Actor exampleActor) {
<lineannotation>// notice how the named parameters match the properties of the above '<classname>Actor</classname>' class</lineannotation> <lineannotation>// notice how the named parameters match the properties of the above '<classname>Actor</classname>' class</lineannotation>
String sql = "select count(0) from T_ACTOR where first_name = :firstName and last_name = :lastName"; String sql =
"select count(*) from T_ACTOR where first_name = :firstName and last_name = :lastName";
SqlParameterSource namedParameters = new BeanPropertySqlParameterSource(exampleActor); SqlParameterSource namedParameters = new BeanPropertySqlParameterSource(exampleActor);
@ -560,19 +612,25 @@ public int countOfActors(Actor exampleActor) {
<title><classname>SimpleJdbcTemplate</classname></title> <title><classname>SimpleJdbcTemplate</classname></title>
<note> <note>
<para><emphasis>The functionality offered by the <para><emphasis>The idea behind the
<classname>SimpleJdbcTemplate</classname> is only available to you if <classname>SimpleJdbcTemplate</classname> is to provide a simpler
you are using Java 5 or later.</emphasis></para> 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,
siince 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> </note>
<para>The <classname>SimpleJdbcTemplate</classname> class is a wrapper <para>The <classname>SimpleJdbcTemplate</classname> class is a wrapper
around the classic <classname>JdbcTemplate</classname> that takes around the classic <classname>JdbcTemplate</classname> that takes better
advantage of Java 5 language features such as varargs and autoboxing. advantage of Java 5 language features such as varargs and
The <classname>SimpleJdbcTemplate</classname> class is somewhat of a sop autoboxing.</para>
to the syntactic-sugar-like features of Java 5, but as anyone who has
developed on Java 5 and then had to move back to developing on a
previous version of the JDK will know, those syntactic-sugar-like
features sure are nice.</para>
<para>The value-add of the <classname>SimpleJdbcTemplate</classname> <para>The value-add of the <classname>SimpleJdbcTemplate</classname>
class in the area of syntactic-sugar is best illustrated with a class in the area of syntactic-sugar is best illustrated with a
@ -589,42 +647,12 @@ public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource); this.jdbcTemplate = new JdbcTemplate(dataSource);
} }
public Actor findActor(long id) { public Actor findActor(String specialty, int age) {
String sql = "select id, first_name, last_name from T_ACTOR where id = ?";
RowMapper mapper = new RowMapper() { String sql = "select id, first_name, last_name from T_ACTOR" +
" where specialty = ? and age = ?";
public Object mapRow(ResultSet rs, int rowNum) throws SQLException { RowMapper&lt;Actor&gt; mapper = new RowMapper&lt;Actor&gt;() {
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 cast, the wrapping up of the 'id' argument
// in an array, and the boxing of the 'id' argument as a reference type</lineannotation>
return (Actor) jdbcTemplate.queryForObject(sql, mapper, new Object[] {Long.valueOf(id)});
}</programlisting>
<para>Here is the same method, only this time using the
<classname>SimpleJdbcTemplate</classname>; notice how much 'cleaner' the
code is.</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(long id) {
String sql = "select id, first_name, last_name from T_ACTOR where id = ?";
ParameterizedRowMapper&lt;Actor&gt; mapper = new ParameterizedRowMapper&lt;Actor&gt;() {
<lineannotation>// notice the return type with respect to Java 5 covariant return types</lineannotation>
public Actor mapRow(ResultSet rs, int rowNum) throws SQLException { public Actor mapRow(ResultSet rs, int rowNum) throws SQLException {
Actor actor = new Actor(); Actor actor = new Actor();
actor.setId(rs.getLong("id")); actor.setId(rs.getLong("id"));
@ -634,7 +662,38 @@ public Actor findActor(long id) {
} }
}; };
return this.simpleJdbcTemplate.queryForObject(sql, mapper, id);
<lineannotation>// notice the wrapping up of the argumenta in an array</lineannotation>
return (Actor) jdbcTemplate.queryForObject(sql, new Object[] {id}, mapper);
}</programlisting>
<para>Here is the same method, only this time using the
<classname>SimpleJdbcTemplate</classname>.</para>
<programlisting language="java"><lineannotation>// <classname>SimpleJdbcTemplate</classname>-style...</lineannotation>
private SimpleJdbcTemplate simpleJdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource);
}
public Actor findActor(String specialty, int age) {
String sql = "select id, first_name, last_name from T_ACTOR" +
" where specialty = ? and age = ?";
RowMapper&lt;Actor&gt; mapper = new RowMapper&lt;Actor&gt;() {
public Actor mapRow(ResultSet rs, int rowNum) throws SQLException {
Actor actor = new Actor();
actor.setId(rs.getLong("id"));
actor.setFirstName(rs.getString("first_name"));
actor.setLastName(rs.getString("last_name"));
return actor;
}
};
<lineannotation>// notice the use of varargs since the parameter values now come
// after the RowMapper parameter</lineannotation>
return this.simpleJdbcTemplate.queryForObject(sql, mapper, specialty, age);
}</programlisting> }</programlisting>
<para>See also the section entitled <xref <para>See also the section entitled <xref
@ -675,27 +734,80 @@ public Actor findActor(long id) {
configured.</para> configured.</para>
<para>When using Spring's JDBC layer, you can either obtain a data <para>When using Spring's JDBC layer, you can either obtain a data
source from JNDI or you can configure your own, using an implementation source from JNDI or you can configure your own, using a connection pool
that is provided in the Spring distribution. The latter comes in handy implementation provided by a third party. Popular ones are Apache
for unit testing outside of a web container. We will use the Jakarta Commons DBCP and C3P0. There are some implementations provided
<classname>DriverManagerDataSource</classname> implementation for this in the Spring distribution, but they are only meant to be used for
section but there are several additional implementations that will be testing purposes since they don't provide any pooling. </para>
covered later on. The <classname>DriverManagerDataSource</classname>
works the same way that you probably are used to work when you obtain a <para>We will use the <classname>DriverManagerDataSource</classname>
JDBC connection. You have to specify the fully qualified class name of implementation for this section but there are several additional
the JDBC driver that you are using so that the implementations that will be covered later on. The
<classname>DriverManager</classname> can load the driver class. Then you <classname>DriverManagerDataSource</classname> works the same way that
have to provide a URL that varies between JDBC drivers. You have to you probably are used to work when you obtain a JDBC connection. You
consult the documentation for your driver for the correct value to use have to specify the fully qualified class name of the JDBC driver that
here. Finally you must provide a username and a password that will be you are using so that the <classname>DriverManager</classname> can load
used to connect to the database. Here is an example of how to configure the driver class. Then you have to provide a URL that varies between
a <classname>DriverManagerDataSource</classname>:</para> 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(); <programlisting language="java"><![CDATA[DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.hsqldb.jdbcDriver"); dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
dataSource.setUrl("jdbc:hsqldb:hsql://localhost:"); dataSource.setUrl("jdbc:hsqldb:hsql://localhost:");
dataSource.setUsername("sa"); dataSource.setUsername("sa");
dataSource.setPassword("");]]></programlisting> 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 puposes 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>
<section id="jdbc-SQLExceptionTranslator"> <section id="jdbc-SQLExceptionTranslator">
@ -750,7 +862,7 @@ dataSource.setPassword("");]]></programlisting>
<para><classname>SQLErrorCodeSQLExceptionTranslator</classname> can be <para><classname>SQLErrorCodeSQLExceptionTranslator</classname> can be
extended the following way:</para> extended the following way:</para>
<programlisting language="java"><![CDATA[public class MySQLErrorCodesTranslator extends SQLErrorCodeSQLExceptionTranslator { <programlisting language="java"><![CDATA[public class CustomSQLErrorCodesTranslator extends SQLErrorCodeSQLExceptionTranslator {
protected DataAccessException customTranslate(String task, String sql, SQLException sqlex) { protected DataAccessException customTranslate(String task, String sql, SQLException sqlex) {
if (sqlex.getErrorCode() == -12345) { if (sqlex.getErrorCode() == -12345) {
@ -770,19 +882,26 @@ dataSource.setPassword("");]]></programlisting>
processing where this translator is needed. Here is an example of how processing where this translator is needed. Here is an example of how
this custom translator can be used:</para> this custom translator can be used:</para>
<programlisting language="java"><lineannotation>// create a <classname>JdbcTemplate</classname> and set data source</lineannotation> <programlisting language="java"><lineannotation>private JdbcTemplate jdbcTemoplate;
JdbcTemplate jt = new JdbcTemplate();
jt.setDataSource(dataSource); 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> <lineannotation> // create a custom translator and set the <interfacename>DataSource</interfacename> for the default translation lookup</lineannotation>
MySQLErrorCodesTransalator tr = new MySQLErrorCodesTransalator(); CustomSQLErrorCodesTranslator tr = new CustomSQLErrorCodesTranslator();
tr.setDataSource(dataSource); tr.setDataSource(dataSource);
jt.setExceptionTranslator(tr); this.jdbcTemplate.setExceptionTranslator(tr);
<lineannotation>// use the <classname>JdbcTemplate</classname> for this <classname>SqlUpdate</classname></lineannotation> }
SqlUpdate su = new SqlUpdate();
su.setJdbcTemplate(jt); <lineannotation>public void updateShippingCharge(long orderId, long pct) {
su.setSql("update orders set shipping_charge = shipping_charge * 1.05"); // use the <classname>prepared JdbcTemplate</classname> for this u<classname>pdate</classname></lineannotation>
su.compile(); this.jdbcTemplate.update(
su.update();</programlisting> "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 <para>The custom translator is passed a data source because we still
want the default translation to look up the error codes in want the default translation to look up the error codes in
@ -875,7 +994,7 @@ public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource); this.jdbcTemplate = new JdbcTemplate(dataSource);
} }
public List getList() { public List<Map<String, Object>> getList() {
return this.jdbcTemplate.queryForList("select * from mytable"); return this.jdbcTemplate.queryForList("select * from mytable");
}]]></programlisting> }]]></programlisting>
@ -890,9 +1009,10 @@ public List getList() {
<para>There are also a number of update methods that you can use. Find <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 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 this example an SQL statement is used that has place holders for row
parameters. Note that the parameter values are passed in as an array of parameters. Note that the parameter values can be passed in as varargs
objects (and thus primitives have to be wrapped in the primitive wrapper or alternatively as an array of objects (and thus primitives should
classes).</para> either be wrapped in the primitive wrapper classes either explicitly or
using auto-boxing).</para>
<programlisting language="java"><![CDATA[import javax.sql.DataSource; <programlisting language="java"><![CDATA[import javax.sql.DataSource;
@ -909,7 +1029,7 @@ public class ExecuteAnUpdate {
public void setName(int id, String name) { public void setName(int id, String name) {
this.jdbcTemplate.update( this.jdbcTemplate.update(
"update mytable set name = ? where id = ?", "update mytable set name = ? where id = ?",
new Object[] {name, new Integer(id)}); name, id);
} }
}]]></programlisting> }]]></programlisting>
</section> </section>
@ -1178,14 +1298,14 @@ jdbcTemplate.update(
this.jdbcTemplate = new JdbcTemplate(dataSource); this.jdbcTemplate = new JdbcTemplate(dataSource);
} }
public int[] batchUpdate(final List actors) { public int[] batchUpdate(final List<Actor> actors) {
int[] updateCounts = jdbcTemplate.batchUpdate( int[] updateCounts = jdbcTemplate.batchUpdate(
"update t_actor set first_name = ?, last_name = ? where id = ?", "update t_actor set first_name = ?, last_name = ? where id = ?",
new BatchPreparedStatementSetter() { new BatchPreparedStatementSetter() {
public void setValues(PreparedStatement ps, int i) throws SQLException { public void setValues(PreparedStatement ps, int i) throws SQLException {
ps.setString(1, ((Actor)actors.get(i)).getFirstName()); ps.setString(1, actors.get(i).getFirstName());
ps.setString(2, ((Actor)actors.get(i)).getLastName()); ps.setString(2, actors.get(i).getLastName());
ps.setLong(3, ((Actor)actors.get(i)).getId().longValue()); ps.setLong(3, actors.get(i).getId().longValue());
} }
public int getBatchSize() { public int getBatchSize() {
@ -2510,68 +2630,83 @@ clobReader.close();]]></programlisting>
the stored procedure.</para> the stored procedure.</para>
</section> </section>
</section> </section>
<section id="jdbc-embedded-database-support"> <section id="jdbc-embedded-database-support">
<title>Embedded database support</title> <title>Embedded database support</title>
<para>
The <literal>org.springframework.jdbc.datasource.embedded</literal> package provides support for embedded Java database engines. <para>The <literal>org.springframework.jdbc.datasource.embedded</literal>
Support for <ulink url="http://www.hsqldb.org">HSQL</ulink> and <ulink url="http://www.h2database.com">H2</ulink> is provided natively. package provides support for embedded Java database engines. Support for
There is also an extensible API for plugging in new embedded database types and <classname>DataSource</classname> implementations. <ulink url="http://www.hsqldb.org">HSQL</ulink> and <ulink
</para> 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"> <section id="jdbc-why-embedded-database">
<title>Why use a embedded database?</title> <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. <para>An embedded database is useful during the development phase of a
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. project due to its lightweight nature. Ease of configuration, quick
</para> 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>
<section id="jdbc-embedded-database-xml"> <section id="jdbc-embedded-database-xml">
<title>Creating an embedded database instance using Spring XML</title> <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: <para>When you wish to expose an embedded database instance as a bean in
<programlisting language="xml"><![CDATA[ a Spring ApplicationContext, use the embedded-database tag in the
spring-jdbc namespace: <programlisting language="xml"><![CDATA[
<jdbc:embedded-database id="dataSource"> <jdbc:embedded-database id="dataSource">
<jdbc:script location="classpath:schema.sql"/> <jdbc:script location="classpath:schema.sql"/>
<jdbc:script location="classpath:test-data.sql"/> <jdbc:script location="classpath:test-data.sql"/>
</jdbc:embedded-database>]]> </jdbc:embedded-database>
</programlisting> ]]></programlisting></para>
</para>
<para> <para>The configuration above creates an embedded HSQL database
The configuration above creates an embedded HSQL database populated with SQL from schema.sql and testdata.sql resources in the classpath. populated with SQL from schema.sql and testdata.sql resources in the
The database instance is made available to the Spring container as a bean of type <classname>javax.sql.DataSource</classname>. classpath. The database instance is made available to the Spring
This bean can then be injected into data access objects as needed. container as a bean of type <classname>javax.sql.DataSource</classname>.
</para> This bean can then be injected into data access objects as
needed.</para>
</section> </section>
<section id="jdbc-embedded-database-java"> <section id="jdbc-embedded-database-java">
<title> <title>Creating an embedded database instance programatically</title>
Creating an embedded database instance programatically
</title> <para>The <classname>EmbeddedDatabaseBuilder</classname> class provides
<para> a fluent API for constructing an embedded database programmatically. Use
The <classname>EmbeddedDatabaseBuilder</classname> class provides a fluent API for constructing an embedded database programmatically. this when you need to create an embedded database instance in a
Use this when you need to create an embedded database instance in a standalone environment, such as a data access object unit test: standalone environment, such as a data access object unit test:
<programlisting language="java"><![CDATA[ <programlisting language="java"><![CDATA[
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder(); EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
EmbeddedDatabase db = builder.type(H2).script("schema.sql").script("test-data.sql").build(); EmbeddedDatabase db = builder.type(H2).script("schema.sql").script("test-data.sql").build();
// do stuff against the db (EmbeddedDatabase extends javax.sql.DataSource) // do stuff against the db (EmbeddedDatabase extends javax.sql.DataSource)
db.shutdown()]]> db.shutdown()
</programlisting> ]]></programlisting></para>
</para>
</section> </section>
<section id="jdbc-embedded-database-extension"> <section id="jdbc-embedded-database-extension">
<title>Extending the embedded database support</title> <title>Extending the embedded database support</title>
<para>
Spring Jdbc's embedded database support can be extended in two ways: <para>Spring Jdbc's embedded database support can be extended in two
<orderedlist> ways: <orderedlist>
<listitem> <listitem>
<para>Implement <classname>EmbeddedDatabaseConfigurer</classname> to support a new embedded database type, such as Apache Derby.</para> <para>Implement <classname>EmbeddedDatabaseConfigurer</classname>
to support a new embedded database type, such as Apache
Derby.</para>
</listitem> </listitem>
<listitem> <listitem>
<para>Implement <classname>DataSourceFactory</classname> to support a new DataSource implementation, such as a connection pool, to manage embedded database connections.</para> <para>Implement <classname>DataSourceFactory</classname> to
support a new DataSource implementation, such as a connection
pool, to manage embedded database connections.</para>
</listitem> </listitem>
</orderedlist> </orderedlist></para>
</para>
<para> <para>You are encouraged to contribute back extensions to the Spring
You are encouraged to contribute back extensions to the Spring community at <ulink url="jira.springframework.org">jira.springframework.org</ulink>. community at <ulink
</para> url="jira.springframework.org">jira.springframework.org</ulink>.</para>
</section> </section>
</section> </section>
</chapter> </chapter>