IntroductionThe Data Access Object (DAO) support in Spring is aimed at making it
- easy to work with data access technologies like JDBC, Hibernate or JDO in
- a consistent way. This allows one to switch between the aforementioned
- persistence technologies fairly easily and it also allows one to code
- without worrying about catching exceptions that are specific to each
- technology.
+ easy to work with data access technologies like JDBC, Hibernate, JPA or
+ JDO in a consistent way. This allows one to switch between the
+ aforementioned persistence technologies fairly easily and it also allows
+ one to code without worrying about catching exceptions that are specific
+ to each technology.
diff --git a/spring-framework-reference/src/jdbc.xml b/spring-framework-reference/src/jdbc.xml
index 04ce9ab485c..4dc0f035b08 100644
--- a/spring-framework-reference/src/jdbc.xml
+++ b/spring-framework-reference/src/jdbc.xml
@@ -1,7 +1,6 @@
-
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
Data access using JDBC
@@ -73,8 +72,8 @@
JdbcTemplate - this is the
classic Spring JDBC approach and the most widely used. This is the
"lowest level" approach and all other approaches use a JdbcTemplate
- under the covers. Works well in a JDK 1.4 and higher
- environment.
+ under the covers. In Spring 3.0 it has been updated with Java 5
+ support like generics and vararg support.
@@ -82,16 +81,17 @@
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. Works with JDK 1.4 and up.
+ parameters for an SQL statement. It has also been updated with Java
+ 5 support like generics and vararg support for Spring 3.0.
SimpleJdbcTemplate - this
class combines the most frequently used features of both
JdbcTemplate and NamedParameterJdbcTemplate plus it adds additional
- convenience by taking advantage of some Java 5 features like
- varargs, autoboxing and generics to provide an easier to use API.
- Requires JDK 5 or higher.
+ convenience by taking better advantage of Java 5 varargs for
+ ceratain methods where this wasn't possible in the JdbcTemplate due
+ to backwars compatibility reasons.
@@ -101,8 +101,8 @@
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 JDK 5 or higher and a database that
- provides adequate metadata.
+ SimpleJdbcTemplate. Requires a database that provides adequate
+ metadata.
@@ -112,8 +112,9 @@
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. Works with
- JDK 1.4 and higher.
+ 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.
@@ -231,48 +232,48 @@
A simple query for getting the number of rows in a
relation.
- int rowCount = this.jdbcTemplate.queryForInt("select count(0) from t_accrual");
+ A simple query using a bind variable.
- int countOfActorsNamedJoe = this.jdbcTemplate.queryForInt(
- "select count(0) from t_actors where first_name = ?", new Object[]{"Joe"});
+ Querying for a String.
- String surname = (String) this.jdbcTemplate.queryForObject(
+
+ new Object[]{1212L}, String.class);]]>Querying and populating a single domain
object.
- Actor actor = (Actor) this.jdbcTemplate.queryForObject(
- "select first_name, surname from t_actor where id = ?",
- new Object[]{new Long(1212)},
- new RowMapper() {
-
- public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
- Actor actor = new Actor();
- actor.setFirstName(rs.getString("first_name"));
- actor.setSurname(rs.getString("surname"));
- return actor;
- }
- });
+ () {
+ public Actor mapRow(ResultSet rs, int rowNum) throws SQLException {
+ Actor actor = new Actor();
+ actor.setFirstName(rs.getString("first_name"));
+ actor.setSurname(rs.getString("surname"));
+ return actor;
+ }
+ });
+]]>Querying and populating a number of domain objects.
- Collection actors = this.jdbcTemplate.query(
- "select first_name, surname from t_actor",
- new RowMapper() {
-
- public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
- Actor actor = new Actor();
- actor.setFirstName(rs.getString("first_name"));
- actor.setSurname(rs.getString("surname"));
- return actor;
- }
- });
+ actors = this.jdbcTemplate.query(
+ "select first_name, surname from t_actor",
+ new RowMapper() {
+ public Actor mapRow(ResultSet rs, int rowNum) throws SQLException {
+ Actor actor = new Actor();
+ actor.setFirstName(rs.getString("first_name"));
+ actor.setSurname(rs.getString("surname"));
+ return actor;
+ }
+ });
+]]>If the last two snippets of code actually existed in the same
application, it would make sense to remove the duplication present
@@ -282,35 +283,35 @@
by DAO methods as needed. For example, the last code snippet might
be better off written like so:
- public Collection findAllActors() {
+ findAllActors() {
return this.jdbcTemplate.query( "select first_name, surname from t_actor", new ActorMapper());
}
-private static final class ActorMapper implements RowMapper {
+private static final class ActorMapper implements RowMapper {
- public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
+ public Actor mapRow(ResultSet rs, int rowNum) throws SQLException {
Actor actor = new Actor();
actor.setFirstName(rs.getString("first_name"));
actor.setSurname(rs.getString("surname"));
return actor;
- }
-}
+ }
+}]]>Updating (INSERT/UPDATE/DELETE)
- this.jdbcTemplate.update(
+
+ new Object[] {"Leonor", "Watling"});]]>
- this.jdbcTemplate.update(
+
+ new Object[] {"Banjo", new Long(5276)});]]>
- this.jdbcTemplate.update(
+
+ new Object[] {new Long.valueOf(actorId)});]]>
@@ -321,15 +322,15 @@ private static final class ActorMapper implements RowMapper {
statements. It is heavily overloaded with variants taking callback
interfaces, binding variable arrays, and suchlike.
- this.jdbcTemplate.execute("create table mytable (id integer, name varchar(100))");
+ Invoking a simple stored procedure (more sophisticated stored
procedure support is covered
later).
- this.jdbcTemplate.update(
+
+ new Object[]{Long.valueOf(unionId)});]]>
@@ -693,11 +694,11 @@ public Actor findActor(long id) {
used to connect to the database. Here is an example of how to configure
a DriverManagerDataSource:
- DriverManagerDataSource dataSource = new DriverManagerDataSource();
+
+dataSource.setPassword("");]]>
@@ -752,7 +753,7 @@ dataSource.setPassword("");SQLErrorCodeSQLExceptionTranslator can be
extended the following way:
- public class MySQLErrorCodesTranslator extends SQLErrorCodeSQLExceptionTranslator {
+
}
return null;
}
-}
+}]]>In this example the specific error code
'-12345' is translated and any other errors are
@@ -802,7 +803,7 @@ su.update();
what you need to include for a minimal but fully functional class that
creates a new table.
- import javax.sql.DataSource;
+
+}]]>
@@ -836,7 +837,7 @@ public class ExecuteAStatement {
an int and one that queries for a
String.
- import javax.sql.DataSource;
+
+}]]>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
@@ -870,7 +871,7 @@ public class RunAQuery {
above example to retrieve a list of all the rows, it would look like
this:
-
+
+}]]>The list returned would look something like this:
- [{name=Bob, id=1}, {name=Mary, id=2}]
+
@@ -896,7 +897,7 @@ public List getList() {
objects (and thus primitives have to be wrapped in the primitive wrapper
classes).
- import javax.sql.DataSource;
+
+}]]>
@@ -1016,8 +1017,9 @@ jdbcTemplate.update(
The DriverManagerDataSource class is an
implementation of the standard DataSource
- interface that configures a plain old JDBC Driver via bean properties, and
- returns a new Connection every time.
+ interface that configures a plain old JDBC Driver via bean properties,
+ and returns a new Connection every
+ time.
This is potentially useful for test or standalone environments
outside of a J2EE container, either as a
@@ -1172,7 +1174,7 @@ jdbcTemplate.update(
where we update the actor table based on entries in a list. The entire
list is used as the batch in his example.
- public class JdbcActorDao implements ActorDao {
+ If you are processing stream of updates or reading from a
+}]]>If you are processing stream of updates or reading from a
file then you might have a preferred batch size, but the last batch
might not have that number of entries. In this case you can use the
InterruptibleBatchPreparedStatementSetter
@@ -1223,14 +1225,14 @@ jdbcTemplate.update(
This example shows a batch update using named parameters:
- public class JdbcActorDao implements ActorDao {
+ actors) {
SqlParameterSource[] batch = SqlParameterSourceUtils.createBatch(actors.toArray());
int[] updateCounts = simpleJdbcTemplate.batchUpdate(
"update t_actor set first_name = :firstName, last_name = :lastName where id = :id",
@@ -1239,23 +1241,23 @@ jdbcTemplate.update(
}
// ... additional methods
-}For an SQL statement using the classic "?" place holders you
- pass in a List containing an object array with the update values. This
- object array must have one entry for each placeholder in the SQL
+}]]>For an SQL statement using the classic "?" place holders
+ you pass in a List containing an object array with the update values.
+ This object array must have one entry for each placeholder in the SQL
statement and they must be in the same order as they are defined in the
SQL statement.The same example using classic JDBC "?" place holders:
- public class JdbcActorDao implements ActorDao {
+ actors) {
+ ListAll batch update methods return an int array containing the
- number of affected rows for each batch entry. This count is reported by
- the JDBC driver and it's not always available in which case the JDBC
+}]]>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.
@@ -1304,7 +1306,7 @@ jdbcTemplate.update(
configuration methods. In this case there is only one configuration
method used but we will see examples of multiple ones soon.
- public class JdbcActorDao implements ActorDao {
+ parameters = new HashMap(3);
parameters.put("id", actor.getId());
parameters.put("first_name", actor.getFirstName());
parameters.put("last_name", actor.getLastName());
@@ -1323,7 +1325,7 @@ jdbcTemplate.update(
}
// ... additional methods
-}
+}]]>The execute method used here takes a plain
java.utils.Map as its only parameter. The
@@ -1343,7 +1345,7 @@ jdbcTemplate.update(
generated key column using the
usingGeneratedKeyColumns method.
- public class JdbcActorDao implements ActorDao {
+ parameters = new HashMap(2);
parameters.put("first_name", actor.getFirstName());
parameters.put("last_name", actor.getLastName());
Number newId = insertActor.executeAndReturnKey(parameters);
@@ -1364,7 +1366,7 @@ jdbcTemplate.update(
}
// ... additional methods
-}Here we can see the main difference when executing the
+}]]>Here we can see the main difference when executing the
insert is that we don't add the id to the Map and we call the
executeReturningKey method. This returns a
java.lang.Number object that we can use to create an
@@ -1384,7 +1386,7 @@ jdbcTemplate.update(
specifying a list of column names to be used. This is accomplished using
the usingColumns method.
- public class JdbcActorDao implements ActorDao {
+ parameters = new HashMap(2);
parameters.put("first_name", actor.getFirstName());
parameters.put("last_name", actor.getLastName());
Number newId = insertActor.executeAndReturnKey(parameters);
@@ -1406,8 +1408,8 @@ jdbcTemplate.update(
}
// ... additional methods
-}The execution of the insert is the same as if we had relied
- on the metadata for determining what columns to use.
+}]]>The execution of the insert is the same as if we had
+ relied on the metadata for determining what columns to use.
@@ -1422,7 +1424,7 @@ jdbcTemplate.update(
contains your values. It will use the corresponding getter method to
extract the parameter values. Here is an example:
- public class JdbcActorDao implements ActorDao {
+ Another option is the
+}]]>Another option is the
MapSqlParameterSource that resembles a Map but
provides a more convenient addValue method that
can be chained.
- public class JdbcActorDao implements ActorDao {
+ As you can see, the configuration is the same, it;s just the
- executing code that has to change to use these alternative input
+}]]>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.
@@ -1491,7 +1493,7 @@ jdbcTemplate.update(
the source for the procedure as it would look when using MySQL as the
database:
- CREATE PROCEDURE read_actor (
+ As you can see there are four parameters. One is an in
+END;]]>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.
@@ -1510,7 +1512,7 @@ END;As you can see there are four parameters. One is an in
subclass and we declare it in the initialization method. For this
example, all we need to specify is the name of the procedure.
- public class JdbcActorDao implements ActorDao {
+ As you can see there are four parameters. One is an in
}
// ... additional methods
-}The execution of the call involves creating an
+}]]>The execution of the call involves creating an
SqlParameterSource containing the in parameter.
It's important to match the name of the parameter declared in the stored
procedure. The case doesn't have to match since we use metadata to
@@ -1565,7 +1567,7 @@ END;As you can see there are four parameters. One is an in
commons-collections.jar on your classpath for
this to work. Here is an example of this configuration:
- public class JdbcActorDao implements ActorDao {
+ As you can see there are four parameters. One is an in
// ... additional methods
-}By doing this, you don't have to worry about the case used
- for the names of your returned out parameters.
+}]]>By doing this, you don't have to worry about the case
+ used for the names of your returned out parameters.
@@ -1606,7 +1608,7 @@ END;As you can see there are four parameters. One is an in
This is what a fully declared procedure call declaration of our
earlier example would look like:
- public class JdbcActorDao implements ActorDao {
+ As you can see there are four parameters. One is an in
// ... additional methods
-}The execution and end results are the same, we are just
+}]]>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
@@ -1647,8 +1649,8 @@ END;As you can see there are four parameters. One is an in
java.sql.Types constants. We have already seen
declarations like:
- new SqlParameter("in_id", Types.NUMERIC),
- new SqlOutParameter("out_first_name", Types.VARCHAR),
+ The first line with the SqlParameter
declares an in parameter. In parameters can be used for both stored
@@ -1668,7 +1670,7 @@ END;As you can see there are four parameters. One is an in
input values. This is different from the
StoredProcedure class which for backwards
compatibility reasons allows input values to be provided for
- parameters declared as SqlOutParameter.
+ parameters declared as SqlOutParameter.
In addition to the name and the SQL type you can specify
@@ -1699,7 +1701,7 @@ END;As you can see there are four parameters. One is an in
that returns an actor's full name. Here is the MySQL source for this
function:
- CREATE FUNCTION get_actor_name (in_id INTEGER)
+
+END;]]>To call this function we again create a
SimpleJdbcCall in the initialization
method.
- public class JdbcActorDao implements ActorDao {
+
}
// ... additional methods
-}The execute method used returns a
+}]]>The execute method used returns a
String containing the return value from the
function call.
@@ -1761,17 +1763,17 @@ END;
parameters and returns all rows from the t_actor table. Here is the
MySQL source for this procedure:
- CREATE PROCEDURE read_all_actors()
+ In order to call this procedure we need to declare the
+END;]]>In order to call this procedure we need to declare the
RowMapper to be used. Since the class we want to
map to follows the JavaBean rules, we can use a
ParameterizedBeanPropertyRowMapper that is
created by passing in the required class to map to in the
newInstance method.
- public class JdbcActorDao implements ActorDao {
+ In order to call this procedure we need to declare the
}
public List getActorsList() {
- Map m = procReadAllActors.execute(new HashMap<String, Object>(0));
+ Map m = procReadAllActors.execute(new HashMap(0));
return (List) m.get("actors");
}
// ... additional methods
-}The execute call passes in an empty Map since this call
+}]]>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.
@@ -1855,7 +1857,7 @@ END;In order to call this procedure we need to declare the
customer relation to an instance of the Customer
class.
- private class CustomerMappingQuery extends MappingSqlQuery {
+ In order to call this procedure we need to declare the
cust.setName(rs.getString("name"));
return cust;
}
-}
+}]]>We provide a constructor for this customer query that takes the
DataSource as the only parameter. In this
@@ -1886,18 +1888,18 @@ END;In order to call this procedure we need to declare the
have been defined we call the compile() method so the
statement can be prepared and later be executed.
- public Customer getCustomer(Integer id) {
+ 0) {
return (Customer) customers.get(0);
}
else {
return null;
}
-}
+}]]>The method in this example retrieves the customer with the id that
is passed in as the only parameter. After creating an instance of the
@@ -1975,8 +1977,8 @@ public class UpdateCreditRating extends SqlUpdate {
SQL type is specified using the java.sql.Types
constants. We have already seen declarations like:
- new SqlParameter("in_id", Types.NUMERIC),
- new SqlOutParameter("out_first_name", Types.VARCHAR),
+ The first line with the SqlParameter
declares an in parameter. In parameters can be used for both stored
@@ -1991,11 +1993,11 @@ public class UpdateCreditRating extends SqlUpdate {
that also return a value
- Parameters declared as SqlParameter
- and SqlInOutParameter will always be used to
+ Parameters declared as SqlParameter and
+ SqlInOutParameter will always be used to
provide input values. In addition to this any parameter declared as
SqlOutParameter where an non-null input value
- is provided will also be used as an input paraneter.
+ is provided will also be used as an input paraneter.In addition to the name and the SQL type you can specify
@@ -2113,7 +2115,7 @@ public class TitlesAndGenresStoredProcedure extends StoredProcedure {
Title domain object for each row in the supplied
ResultSet.
- import com.foo.sprocs.domain.Title;
+
+}]]>Secondly, the GenreMapper class, which
again simply maps a ResultSet to a
Genre domain object for each row in the supplied
ResultSet.
- import org.springframework.jdbc.core.RowMapper;
+
+}]]>If one needs to pass parameters to a stored procedure (that is the
stored procedure has been declared as having one or more input
@@ -2155,7 +2157,7 @@ public final class GenreMapper implements RowMapper {
superclass' (untyped) execute(Map parameters) (which
has protected access); for example:
- import oracle.jdbc.driver.OracleTypes;
+
+}]]>
@@ -2211,11 +2213,11 @@ public class TitlesAfterDateStoredProcedure extends StoredProcedure {
the appropriate run method repeatedly to execute the function. Here is
an example of retrieving the count of rows from a table:
- public int countRows() {
+
+}]]>
@@ -2346,7 +2348,7 @@ public class TitlesAfterDateStoredProcedure extends StoredProcedure {
- final File blobIn = new File("spring2004.jpg");
+
+clobReader.close();]]>
@@ -2395,7 +2397,7 @@ clobReader.close();
- List l = jdbcTemplate.query("select id, a_clob, a_blob from lob_table",
+
return results;
}
});
-
+]]>
@@ -2474,7 +2476,7 @@ clobReader.close();
interface is used as part of the declaration of an
SqlOutParameter.
- declareParameter(new SqlOutParameter("item", OracleTypes.STRUCT, "ITEM_TYPE",
+
item.setExpirationDate((java.util.Date)attr[2]);
return item;
}
- }));Going from Java to the database and passing in the
+ }));]]>Going from Java to the database and passing in the
value of a TestItem into a stored procedure is
done using the SqlTypeValue. The
SqlTypeValue interface has a single method named
@@ -2495,7 +2497,7 @@ clobReader.close();
specific objects like StructDescriptors or
ArrayDescriptors
- SqlTypeValue value = new AbstractSqlTypeValue() {
+
});
return item;
}
-};This SqlTypeValue can now be added
- to the Map containing the input parameters for the execute call of the
- stored procedure.
+};]]>This SqlTypeValue can now be
+ added to the Map containing the input parameters for the execute call of
+ the stored procedure.
\ No newline at end of file