From 95213d488cb058dc82ea78b875b7ee66c7185041 Mon Sep 17 00:00:00 2001 From: Thomas Risberg Date: Tue, 12 May 2009 18:18:29 +0000 Subject: [PATCH] updating code examples with generics/varargs; polishing --- spring-framework-reference/src/jdbc.xml | 519 +++++++++++++++--------- 1 file changed, 327 insertions(+), 192 deletions(-) diff --git a/spring-framework-reference/src/jdbc.xml b/spring-framework-reference/src/jdbc.xml index 088222e4ac9..efa74b10cad 100644 --- a/spring-framework-reference/src/jdbc.xml +++ b/spring-framework-reference/src/jdbc.xml @@ -73,7 +73,7 @@ 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. + support like generics and vararg support. @@ -88,9 +88,10 @@ SimpleJdbcTemplate - 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. + 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. @@ -141,9 +142,11 @@ contains a utility class for easy DataSource access, and various simple DataSource implementations that can be - used for testing and running unmodified JDBC code outside of a Java EE container. - A sub-package named org.springfamework.jdbc.datasource.embedded - provides support for creating in-memory database instances using Java database engines such as HSQL and H2. + used for testing and running unmodified JDBC code outside of a Java EE + container. A sub-package named + org.springfamework.jdbc.datasource.embedded provides + support for creating in-memory database instances using Java database + engines such as HSQL and H2. Next, the org.springframework.jdbc.object package contains classes that represent RDBMS queries, updates, and @@ -238,21 +241,21 @@ Querying for a String. - Querying and populating a single domain object. () { public Actor mapRow(ResultSet rs, int rowNum) throws SQLException { Actor actor = new Actor(); actor.setFirstName(rs.getString("first_name")); - actor.setSurname(rs.getString("surname")); + actor.setLastName(rs.getString("last_name")); return actor; } }); @@ -261,12 +264,12 @@ Querying and populating a number of domain objects. actors = this.jdbcTemplate.query( - "select first_name, surname from t_actor", + "select first_name, last_name 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")); + actor.setLastName(rs.getString("last_name")); return actor; } }); @@ -281,7 +284,7 @@ be better off written like so: 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 { @@ -289,7 +292,7 @@ private static final class ActorMapper implements 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")); + actor.setLastName(rs.getString("last_name")); return actor; } }]]> @@ -299,16 +302,16 @@ private static final class ActorMapper implements RowMapper { Updating (INSERT/UPDATE/DELETE) + "insert into t_actor (first_name, last_name) values (?, ?)", + "Leonor", "Watling");]]> + "update t_actor set = ? where id = ?", + "Banjo", 5276L);]]> + Long.valueOf(actorId));]]>
@@ -327,7 +330,7 @@ private static final class ActorMapper implements RowMapper { + Long.valueOf(unionId));]]>
@@ -368,37 +371,85 @@ private static final class ActorMapper implements RowMapper { // JDBC-backed implementations of the methods on the CorporateEventDao follow... } - The attendant configuration might look like this. + The corresponding configuration might look like this. - <?xml version="1.0" encoding="UTF-8"?> -<beans xmlns="http://www.springframework.org/schema/beans" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://www.springframework.org/schema/beans - http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> + + + + + + + + + + + + + - <bean id="corporateEventDao" class="com.example.JdbcCorporateEventDao"> - <property name="dataSource" ref="dataSource"/> - </bean> + - <!-- the DataSource (parameterized for configuration via a PropertyPlaceHolderConfigurer) --> - <bean id="dataSource" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource"> - <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> +]]> -</beans> + An alternative to explicit configuration is to use the component + scanning and annotation support for dependency injection. In this case + we woul annotate the setter method for the + DataSource with the + @Autowired annotation. - If you are using Spring's JdbcDaoSupport - class, and your various JDBC-backed DAO classes extend from it, then - you inherit a setDataSource(..) 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 JdbcDaoSupport - class you will see that there is not a whole lot to it... it is - provided as a convenience only. + public class JdbcCorporateEventDao implements CorporateEventDao { + + private JdbcTemplate jdbcTemplate; + + @Autowired + public void setDataSource(DataSource dataSource) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + } + + // JDBC-backed implementations of the methods on the CorporateEventDao follow... +} + + The corresponding XML configuration file would look like the + following: + + + + + + + + + + + + + + + + +]]>If you are using Spring's + JdbcDaoSupport class, and your various + JDBC-backed DAO classes extend from it, then you inherit a + setDataSource(..) 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 JdbcDaoSupport class you will + see that there is not a whole lot to it... it is provided as a + convenience only. Regardless of which of the above template initialization styles 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) { - 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); @@ -468,7 +519,7 @@ public void setDataSource(DataSource dataSource) { 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); @@ -531,7 +582,8 @@ public void setDataSource(DataSource dataSource) { public int countOfActors(Actor exampleActor) { // notice how the named parameters match the properties of the above 'Actor' class - 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); @@ -560,19 +612,25 @@ public int countOfActors(Actor exampleActor) { <classname>SimpleJdbcTemplate</classname> - The functionality offered by the - SimpleJdbcTemplate is only available to you if - you are using Java 5 or later. + The idea behind the + SimpleJdbcTemplate is to provide a simpler + interface that takes better advantage of Java 5 features. It was + initially the only JdbcOperations + implementation that provided support for Java 5 enhanced syntax with + generics and varargs. Now, in Spring 3.0, the original + JdbcTemplate has been upgraded to Java 5 as + well, but the SimpleJdbcTemplate still has the + advantage of providing a simpler API when you don't need access to all + the methods that the JdbcTemplate offers. Also, + siince the SimpleJdbcTemplate was designed for + Java 5 there are more methods that take advantage of varargs due to + different ordering of the parameters. The SimpleJdbcTemplate class is a wrapper - around the classic JdbcTemplate that takes - advantage of Java 5 language features such as varargs and autoboxing. - The SimpleJdbcTemplate class is somewhat of a sop - 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. + around the classic JdbcTemplate that takes better + advantage of Java 5 language features such as varargs and + autoboxing. The value-add of the SimpleJdbcTemplate 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); } -public Actor findActor(long id) { - String sql = "select id, first_name, last_name from T_ACTOR where id = ?"; +public Actor findActor(String specialty, int age) { + + String sql = "select id, first_name, last_name from T_ACTOR" + + " where specialty = ? and age = ?"; - RowMapper mapper = new RowMapper() { - - public Object 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; - } - }; - - // notice the cast, the wrapping up of the 'id' argument - // in an array, and the boxing of the 'id' argument as a reference type - return (Actor) jdbcTemplate.queryForObject(sql, mapper, new Object[] {Long.valueOf(id)}); -} - - Here is the same method, only this time using the - SimpleJdbcTemplate; notice how much 'cleaner' the - code is. - - // SimpleJdbcTemplate-style... -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<Actor> mapper = new ParameterizedRowMapper<Actor>() { - - // notice the return type with respect to Java 5 covariant return types + RowMapper<Actor> mapper = new RowMapper<Actor>() { public Actor mapRow(ResultSet rs, int rowNum) throws SQLException { Actor actor = new Actor(); actor.setId(rs.getLong("id")); @@ -634,7 +662,38 @@ public Actor findActor(long id) { } }; - return this.simpleJdbcTemplate.queryForObject(sql, mapper, id); + + // notice the wrapping up of the argumenta in an array + return (Actor) jdbcTemplate.queryForObject(sql, new Object[] {id}, mapper); +} + + Here is the same method, only this time using the + SimpleJdbcTemplate. + + // SimpleJdbcTemplate-style... +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; + } + }; + + // notice the use of varargs since the parameter values now come + // after the RowMapper parameter + return this.simpleJdbcTemplate.queryForObject(sql, mapper, specialty, age); } See also the section entitled When using Spring's JDBC layer, you can either obtain a data - source from JNDI or you can configure your own, using an implementation - that is provided in the Spring distribution. The latter comes in handy - for unit testing outside of a web container. We will use the - DriverManagerDataSource implementation for this - section but there are several additional implementations that will be - covered later on. The DriverManagerDataSource - 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 - DriverManager 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 DriverManagerDataSource: + 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. + + We will use the DriverManagerDataSource + implementation for this section but there are several additional + implementations that will be covered later on. The + DriverManagerDataSource 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 DriverManager 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 + DriverManagerDataSource in Java code: + + We also have an example how this would look in an XML + configuration: + + + + + + + + +]]> + + + The DriverManagerDataSource 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. + + + 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. + + FIrst we have the DBCP configuration: + + + + + + + + +]]> + + And last we have the C3P0 configuration: + + + + + + + + +]]>
@@ -750,7 +862,7 @@ dataSource.setPassword("");]]> SQLErrorCodeSQLExceptionTranslator can be extended the following way: - processing where this translator is needed. Here is an example of how this custom translator can be used: - // create a JdbcTemplate and set data source -JdbcTemplate jt = new JdbcTemplate(); -jt.setDataSource(dataSource); -// create a custom translator and set the DataSource for the default translation lookup -MySQLErrorCodesTransalator tr = new MySQLErrorCodesTransalator(); -tr.setDataSource(dataSource); -jt.setExceptionTranslator(tr); -// use the JdbcTemplate for this SqlUpdate -SqlUpdate su = new SqlUpdate(); -su.setJdbcTemplate(jt); -su.setSql("update orders set shipping_charge = shipping_charge * 1.05"); -su.compile(); -su.update(); + private JdbcTemplate jdbcTemoplate; + +public void setDataSource(DataSource dataSource) { + // create a JdbcTemplate and set data source + this.jdbcTemplate = new JdbcTemplate(); + this.jdbcTemplate.setDataSource(dataSource); + // create a custom translator and set the DataSource for the default translation lookup + CustomSQLErrorCodesTranslator tr = new CustomSQLErrorCodesTranslator(); + tr.setDataSource(dataSource); + this.jdbcTemplate.setExceptionTranslator(tr); +} + +public void updateShippingCharge(long orderId, long pct) { + // use the prepared JdbcTemplate for this update + this.jdbcTemplate.update( + "update orders" + + " set shipping_charge = shipping_charge * ? / 100" + + " where id = ?" + pct, orderId); +} The custom translator is passed a data source because we still 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); } -public List getList() { +public List> getList() { return this.jdbcTemplate.queryForList("select * from mytable"); }]]> @@ -890,9 +1009,10 @@ public List getList() { 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 are passed in as an array of - objects (and thus primitives have to be wrapped in the primitive wrapper - classes). + 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).
@@ -1178,14 +1298,14 @@ jdbcTemplate.update( this.jdbcTemplate = new JdbcTemplate(dataSource); } - public int[] batchUpdate(final List actors) { + public int[] batchUpdate(final List 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, ((Actor)actors.get(i)).getFirstName()); - ps.setString(2, ((Actor)actors.get(i)).getLastName()); - ps.setLong(3, ((Actor)actors.get(i)).getId().longValue()); + 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() { @@ -1232,8 +1352,8 @@ jdbcTemplate.update( public int[] batchUpdate(final List 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); + "update t_actor set first_name = :firstName, last_name = :lastName where id = :id", + batch); return updateCounts; } @@ -2510,68 +2630,83 @@ clobReader.close();]]> the stored procedure.
+
- Embedded database support - - The org.springframework.jdbc.datasource.embedded package provides support for embedded Java database engines. - Support for HSQL and H2 is provided natively. - There is also an extensible API for plugging in new embedded database types and DataSource implementations. - -
- Why use a embedded database? - - 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. - -
-
- Creating an embedded database instance using Spring XML - - 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: - Embedded database support + + The org.springframework.jdbc.datasource.embedded + package provides support for embedded Java database engines. Support for + HSQL and H2 is provided natively. There is + also an extensible API for plugging in new embedded database types and + DataSource implementations. + +
+ Why use a embedded database? + + 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. +
+ +
+ Creating an embedded database instance using Spring XML + + 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: - ]]> - - - - 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 javax.sql.DataSource. - This bean can then be injected into data access objects as needed. - -
-
- - Creating an embedded database instance programatically - - - The EmbeddedDatabaseBuilder 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: - + ]]> + + 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 javax.sql.DataSource. + This bean can then be injected into data access objects as + needed. +
+ +
+ Creating an embedded database instance programatically + + The EmbeddedDatabaseBuilder 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: + - - -
-
- Extending the embedded database support - - Spring Jdbc's embedded database support can be extended in two ways: - - - Implement EmbeddedDatabaseConfigurer to support a new embedded database type, such as Apache Derby. - - - Implement DataSourceFactory to support a new DataSource implementation, such as a connection pool, to manage embedded database connections. - - - - - You are encouraged to contribute back extensions to the Spring community at jira.springframework.org. - -
+ db.shutdown() + ]]>
+
+ +
+ Extending the embedded database support + + Spring Jdbc's embedded database support can be extended in two + ways: + + Implement EmbeddedDatabaseConfigurer + to support a new embedded database type, such as Apache + Derby. + + + + Implement DataSourceFactory to + support a new DataSource implementation, such as a connection + pool, to manage embedded database connections. + + + + You are encouraged to contribute back extensions to the Spring + community at jira.springframework.org. +
\ No newline at end of file