From 79aae9dde99717ed3309261f959eef3b9b175beb Mon Sep 17 00:00:00 2001 From: Thomas Risberg Date: Sat, 9 May 2009 02:40:54 +0000 Subject: [PATCH] updating code examples with generics/varargs; polishing git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@1136 50f2f4bb-b051-0410-bef5-90022cba6387 --- spring-framework-reference/src/dao.xml | 10 +- spring-framework-reference/src/jdbc.xml | 294 ++++++++++++------------ 2 files changed, 153 insertions(+), 151 deletions(-) diff --git a/spring-framework-reference/src/dao.xml b/spring-framework-reference/src/dao.xml index aa9e7818acd..65900b4b510 100644 --- a/spring-framework-reference/src/dao.xml +++ b/spring-framework-reference/src/dao.xml @@ -8,11 +8,11 @@ Introduction The 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) { + List batch = new ArrayList(); for (Actor actor : actors) { Object[] values = new Object[] { actor.getFirstName(), @@ -1270,9 +1272,9 @@ jdbcTemplate.update( } // ... additional methods -}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 +}]]>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