5.2 JDBC支持
在使用JDBC时,总是要处理繁琐的细节,例如Connection、Statement的获得、SQLException的处理、Connection、Statement的关闭等问题,Spring在JDBC的使用上提供了几个类,可以简化JDBC使用时的流程。
5.2.1 使用JdbcTemplate
在前一节介绍的DataSourceDemo项目中,UserDAO中直接使用JDBC来实现insert() 与find() 方法,当中要处理Connection的取得、Statement的建立、异常的处理、Statement的关闭、Connection的关闭等,对于一个基本的JDBC存取,这些流程都是大同小异的,每一次都必须走这样的流程着实令人厌烦。
Spring提供了org.springframework.jdbc.core.JdbcTemplate类,它被设计为线程安全(Thread-safe),当中所提供的一些操作方法封装了类似以上的流程,例如DataSourceDemo项目中的UserDAO类可以简单地使用JdbcTemplate来改写,要建立JdbcTemplate的实例,必须要有一个DataSource对象作为构造时的对象:
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
来改写一下DataSourceDemo项目中UserDAO的内容实现,可以看到使用JdbcTemplate时,在程序的编写流程上会有什么样的改进:
JdbcTemplateDemo UserDAO.java
package onlyfun.caterpillar;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
public class UserDAO implements IUserDAO {
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
jdbcTemplate = new JdbcTemplate(dataSource);
}
public void insert(User user) {
String name = user.getName();
int age = user.getAge().intValue();
jdbcTemplate.update("INSERT INTO user (name,age) "
+ "VALUES('" + name + "'," + age + ")");
}
public User find(Integer id) {
List rows = jdbcTemplate.queryForList(
"SELECT * FROM user WHERE id=" + id.intValue());
Iterator it = rows.iterator();
if(it.hasNext()) {
Map userMap = (Map) it.next();
Integer i = new Integer(userMap.get("id").toString());
String name = userMap.get("name").toString();
Integer age =
new Integer(userMap.get("age").toString());
User user = new User();
user.setId(i);
user.setName(name);
user.setAge(age);
return user;
}
return null;
}
}
只要改写UserDAO就可以了,其他的程序与设置文件都不用变动,Spring的JdbcTemplate一如其名,主要是通过Template Method模式来实现JDBC的流程封装处理。
良葛格的话匣子<<<
如果使用Hibernate之类的ORM(Object-Relational Mapping)工具,则find()方法中对象模型与关联式模型的转换还可以进一步的简化。
Spring的JDBC封装等功能基本上可以独立于Spring来使用,除了JdbcTemplate之外,Spring还提供了其他的Template类,像是对Hibernate、JDO、iBatis等的Template实现。在数据库的事务处理方面,Spring提供了编程式与声明式的事务管理功能,简化了持久层程序的复杂度,并提供了更好的维护性。
5.2.2 执行与更新JdbcTemplate
您可以使用JdbcTemplate的execute() 方法执行SQL陈述,例如:
jdbcTemplate.execute(
"CREATE TABLE USER (user_id integer, name varchar(100))");
如果是UPDATE或INSERT,您可以使用update() 方法,它有数个重载(Overload)版本,例如接受实现org.springframework.jdbc.core.PreparedStatementCreator接口的对象,PreparedStatementCreator接口的定义如下:
package org.springframework.jdbc.core;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public interface PreparedStatementCreator {
PreparedStatement createPreparedStatement(Connection con)
throws SQLException;
}
例如可以将5.2.1节的JdbcTemplateDemo项目中UserDAO的insert() 方法改写如下:
...
public void insert(User user) {
final String name = user.getName();
final int age = user.getAge().intValue();
jdbcTemplate.update(
new PreparedStatementCreator() {
public PreparedStatement createPreparedStatement(
Connection con) throws SQLException {
String sql =
"INSERT INTO user (name,age) VALUES(?,?)";
PreparedStatement ps =
con.prepareStatement(sql);
ps.setString(1, name);
ps.setInt(2, age);
return ps;
}
});
}
...
在这个例子中,使用了PreparedStatement来准备SQL,JdbcTemplate实现了Template-Callback机制,update()方法中实现了Template流程,而PreparedStatement- Creator则实现了Callback对象,在执行JDBC的流程中,必要时会执行所定义的callback方法,也就是createPreparedStatement()方法,传回一个PreparedStatement对象,以完成update()方法中的Template流程。
与PreparedStatementCreator互补的接口是org.springframework.jdbc.core. PreparedStatementSetter接口:
package org.springframework.jdbc.core;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public interface PreparedStatementSetter {
void setValues(PreparedStatement ps) throws SQLException;
}
例如可以将JdbcTemplateDemo项目中UserDAO的insert() 方法改写如下:
...
public void insert(User user) {
final String name = user.getName();
final int age = user.getAge().intValue();
jdbcTemplate.update(
"INSERT INTO user (name,age) VALUES(?,?)",
new PreparedStatementSetter() {
public void setValues(PreparedStatement ps)
throws SQLException {
ps.setString(1, name);
ps.setInt(2, age);
}
});
}
...
JdbcTemplate会自动建立PreparedStatementCreator的实例,以提供传递给setValues() 方法的PreparedStatement对象。
您也可以直接提供SQL,就如JdbcTemplateDemo项目中所示范的UserDAO中的insert() 方法一样:
...
public void insert(User user) {
String name = user.getName();
int age = user.getAge().intValue();
jdbcTemplate.update("INSERT INTO user (name,age) "
+ "VALUES('" + name + "'," + age + ")");
}
...
在直接下SQL语句时,也可以使用 "?" 作为占位字符,并使用对象数组,把它作为参数传递给JdbcTemplate的update() 方法,例如改写JdbcTemplateDemo项目中所示范的UserDAO中的insert() 方法:
...
public void insert(User user) {
jdbcTemplate.update(
"INSERT INTO user (name, age) VALUES(?,?)",
new Object[] {user.getName(), user.getAge()});
}
...
JdbcTemplate会自动建立PreparedStatementCreator与PreparedStatementSetter的实例,然而这些细节您不用理会,只要提供SQL与参数就好了。
如果需要批处理,可以实现org.springframework.jdbc.core.BatchPrepared- StatementSetter接口:
package org.springframework.jdbc.core;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public interface BatchPreparedStatementSetter {
void setValues(PreparedStatement ps,
int i) throws SQLException;
int getBatchSize();
}
例如可以在JdbcTemplateDemo项目中的IUserDAO接口及UserDAO类上增加一个insertUsers() 方法的定义与实现,如以下所示的内容:
...
public int[] insertUsers(final List users) {
String sql = "INSERT INTO user (name,age) VALUES(?,?)";
BatchPreparedStatementSetter setter =
new BatchPreparedStatementSetter() {
public void setValues(
PreparedStatement ps, int i) throws SQLException {
User user = (User) users.get(i);
ps.setString(1, user.getName());
ps.setInt(2, user.getAge().intValue());
}
public int getBatchSize() {
return users.size();
}
};
return jdbcTemplate.batchUpdate(sql, setter);
}
...
如果JDBC驱动程序支持批处理,则直接使用它的功能,如果不支持, Spring则会一个一个自动处理更新以模拟批处理。
5.2.3 JdbcTemplate查询
使用JdbcTemplate进行查询时,可以使用queryForXXX() 等方法,例如使用queryForInt()方法传回user表格中的数据数目:
jdbcTemplate.queryForInt("SELECT COUNT(*) FROM user");
也可以使用queryForObject()传回一个查询后的结果对象,例如传回一个String对象:
String name = (String) jdbcTemplate.queryForObject(
"SELECT name FROM USER WHERE id = ?",
new Object[] {id},
java.lang.String.class);
上面两个例子传回的都是单独一笔数据,如果要传回多笔数据,则可以使用queryForList() 方法,例如:
List rows = jdbcTemplate.queryForList(
"SELECT * FROM user WHERE id=" + id.intValue());
传回的List中包括的是Map对象,每个Map对象代表查询结果中的一笔数据,每笔数据包括多个字段内容,要取得字段中的值,就要使用字段名称作为"键(Key)",例如:
...
Iterator it = rows.iterator();
while(it.hasNext()) {
Map userMap = (Map) it.next();
System.out.println(userMap.get("id"));
System.out.println(userMap.get("name"));
System.out.println(userMap.get("age"));
...
}
...
您可以实现org.springframework.jdbc.core.RowCallbackHandler接口,在查询到数据之后先作一些处理再传回,例如修改一下5.2.1节的JdbcTemplateDemo项目中UserDAO的find()方法,如下所示,在RowCallback-Handler的processRow()方法中实现简单的ORM(Object-Relational Mapping)动作:
...
public User find(Integer id) {
final User user = new User();
jdbcTemplate.query(
"SELECT * FROM user WHERE id = ?",
new Object[] {id},
new RowCallbackHandler() {
public void processRow(ResultSet rs)
throws SQLException {
user.setId(new Integer(rs.getInt("id")));
user.setName(rs.getString("name"));
user.setAge(new Integer(rs.getInt("age")));
}
});
return user;
}
...
如果一次要取回很多查询结果的对象,则可以先实现org.springframe- work.jdbc.core.RowMapper接口,例如:
package onlyfun.caterpillar;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.jdbc.core.RowMapper;
public class UserRowMapper implements RowMapper {
public Object mapRow(ResultSet rs,
int rowNum) throws SQLException {
User user = new User();
user.setId(new Integer(rs.getInt("id")));
user.setName(rs.getString("name"));
user.setAge(new Integer(rs.getInt("age")));
return user;
}
}
在Spring 2.0中,ResultReader接口与其实现类已经被移除,查询方法现在直接接受RowMapper实例,例如使用queryForObject()方法时:
...
public User find(Integer id) {
User user = (User) jdbcTemplate.queryForObject(
"select * from user where id=?",
new Object[] {id},
new UserRowMapper());
return user;
}
...
传回的结果已使用UserRowMapper的定义,将之封装为User对象。以下是使用query()方法时的一个示范:
List users = jdbcTemplate.query("select * from user",
new UserRowMapper());
for(int i = 0; i < users.size(); i++) {
User user = (User) users.get(i);
System.out.println("/tId:/t" + user.getId());
System.out.println("/tName:/t" + user.getName());
System.out.println("/tAge:/n" + user.getAge());
}
传回的List对象中,包括了从数据库中查询出来的结果,并已封装为User类的实例。
5.2.4 JdbcTemplate的Lob支持
在JDBC中可以使用Clob(Character large object)与Blob(Binary large object),以分别针对文字文件与二进制文件进行储存。Spring中可以通过JdbcTemplate来处理CLOB与BLOB,以避免处理特定数据库(例如Oracle 9i)的Clob、Blob差异问题。举个例子来说,假设您的MySQL数据库表格如下:
CREATE TABLE test (
id INT AUTO_INCREMENT PRIMARY,
txt TEXT,
image BLOB
);
假设现在分别读取一个文字文件与二进制文件,并想将之储存至数据库中,则可以使用JdbcTemplate,例如:
final File binaryFile = new File("wish.jpg");
final File txtFile = new File("test.txt");
final InputStream is = new FileInputStream(binaryFile);
final Reader reader = new FileReader(txtFile);
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
final LobHandler lobHandler = new DefaultLobHandler();
jdbcTemplate.execute("INSERT INTO test (txt, image) VALUES(?, ?)",
new AbstractLobCreatingPreparedStatementCallback(lobHandler) {
protected void setValues(PreparedStatement pstmt,
LobCreator lobCreator) throws SQLException,DataAccessException {
lobCreator.setClobAsCharacterStream(
pstmt, 1, reader, (int) txtFile.length());
lobCreator.setBlobAsBinaryStream(
pstmt, 2, is, (int) binaryFile.length());
}
});
reader.close();
is.close();
在建立AbstractLobCreatingPreparedStatementCallback对象时,要传递一个LobHandler实例,对于MySQL(MS SQL Server或Oracle 10g),使用DefaultLobHandler即可,对于Oracle 9i特定的LOB处理,可以使用OracleLobHandler。在setValues()方法实现中,使用LogCreator来分别设置Blob与Clob的来源串流,索引1、2表示第一与第二个占位字符'?'的位置,并指定读取长度。
如果要从数据库中将数据读取出来,并另存为文件,可以使用以下的程序:
final Writer writer = new FileWriter("test_bak.txt");
final OutputStream os = new FileOutputStream(new File(wish_bak.jpg"));
jdbcTemplate.query("SELECT txt,image FROM test WHERE id = ?",
new Object[] {new Integer(1)},
new AbstractLobStreamingResultSetExtractor() {
protected void streamData(ResultSet rs)
throws SQLException, IOException, DataAccessException {
FileCopyUtils.copy(
lobHandler.getClobAsCharacterStream(rs, 1), writer);
FileCopyUtils.copy(
lobHandler.getBlobAsBinaryStream(rs, 2), os);
}
});
writer.close();
os.close();
在这里使用FileCopyUtils的copy()方法,将LogHandler取得的串流直接转接给文件输出FileWriter、FileOutputStream对象。
5.2.5 以对象方式进行操作
就JdbcTemplate上的各种方法来看,它封装了JDBC处理的细节,让您不用接触底层的数据库技术,然而JdbcTemplate的各种方法仍须熟悉如何使用SQL语法,如果想让一些程序开发人员不用接触到SQL语法或是重用某些SQL,在Spring中,您可以进一步建立可重用的操作对象,建立之后,在设计数据库时,将设计完成的可重用对象给开发人员使用,他们就无须接触到SQL的细节了,从他们的观点来看,数据库操作更接近对象导向的操作方式。
Spring提供了org.springframework.jdbc.object包,可以让您以更对象导向的方式来设计数据库操作方面的程序,只要事先继承或直接实例化相对应的类并进行编译,之后就可以重复利用这个实例,执行它的方法来进行数据库相关操作。
org.springframework.jdbc.object.RdbmsOperation是个抽象类,代表RDBMS(Relational Database Management System)的查询、更新、预存程序等操作,您可以使用其子类org.springframework.jdbc.object.SqlUpdate、org.springframework.jdbc. object.MappingSqlQuery等类,它们被设计为线程安全(Thread-safe),所以可以在多线程的环境下重复使用这些类的实例。
SqlFunction用来执行SQL Function,例如可以执行COUNT(),然后回传一个整数表示查询到的数据笔数,下面是一个例子:
SqlFunction sf = new SqlFunction(
dataSource, "SELECT COUNT(*) from user");
sf.compile();
sf.run();
RdbmsOperation的子类在设置好DataSource、SQL以及相关参数后,必须先执行compile()进行编译,之后就可以重复使用这个实例了。在设计时可以继承SqlFunction来封装SQL,例如修改5.2.1节中JdbcTemplateDemo项目的内容,新增一个类如下所示:
RdbmsDemo UserFunction.java
package onlyfun.caterpillar;
import javax.sql.DataSource;
import org.springframework.jdbc.object.SqlFunction;
public class UserFunction extends SqlFunction {
public UserFunction(DataSource dataSource) {
super(dataSource, "SELECT COUNT(*) from user");
compile();
}
}
SqlUpdate类用来表示一个SQL的更新操作,您也可以设置相关参数,设计时也可以继承它来进行SQL的封装,例如:
RdbmsDemo UserUpdate.java
package onlyfun.caterpillar;
import java.sql.Types;
import javax.sql.DataSource;
import org.springframework.jdbc.object.SqlUpdate;
public class UserUpdate extends SqlUpdate {
public UserUpdate(DataSource dataSource) {
super(dataSource,
"INSERT INTO user (name,age) VALUES(?,?)");
int[] types = {Types.VARCHAR, Types.INTEGER};
setTypes(types);
compile();
}
}
setTypes()方法用来设置SQL中 "?" 占位字符所要插入的数据类型,之后执行update()方法时,可以仅指定对象数组作为参数来进行查询,每一个数组值将实际取代 "?" 占位字符,例如可以这么使用:
...
SqlUpdate userUpdate = new UserUpdate(dataSource);
...
userUpdate.update(
new Object[] {user.getName(), user.getAge()});
SqlQuery类表示一个SQL查询操作,但通常很少直接使用这个类,因为查询到数据之后,会作一些处理。例如封装User类的实例,可以用它的子类org.spring- framework.jdbc.object.MappingSqlMapping来进行这个动作,例如:
RdbmsDemo UserQuery.java
package onlyfun.caterpillar;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.springframework.jdbc.object.MappingSqlQuery;
public class UserQuery extends MappingSqlQuery {
public UserQuery(DataSource dataSource) {
super(dataSource, "SELECT * FROM user");
compile();
}
protected Object mapRow(ResultSet rs,
int rowNum) throws SQLException {
User user = new User();
user.setId(new Integer(rs.getInt("id")));
user.setName(rs.getString("name"));
user.setAge(new Integer(rs.getInt("age")));
return user;
}
}
设计好这个类之后,可以这么使用它:
...
SqlQuery userQuery = new UserQuery(dataSource);
...
List users = userQuery.execute();
执行完execute()方法后,实际上传回的List中会有User类的实例,并且它封装了查询后的每一笔数据。
配合以上的几个实现,可以再改写一下IUserDAO接口与UserDAO实现,例如:
RdbmsDemo IUserDAO.java
package onlyfun.caterpillar;
import java.util.List;
public interface IUserDAO {
public void insert(User user);
public List allUser();
public int count();
}
假设您是程序开发人员,有其他的开发人员负责数据库存取方面的程序,并提供了以上的UserFunction、UserUpdate与UserQuery类,那么您就不用关心实际的SQL是如何编写的了,您可以重用UserFunction、UserUpdate与UserQuery类来设计您的UserDAO类:
RdbmsDemo UserDAO.java
package onlyfun.caterpillar;
import java.util.List;
import javax.sql.DataSource;
import org.springframework.jdbc.object.SqlFunction;
import org.springframework.jdbc.object.SqlQuery;
import org.springframework.jdbc.object.SqlUpdate;
public class UserDAO implements IUserDAO {
private SqlUpdate userUpdate;
private SqlQuery userQuery;
private SqlFunction userFunction;
public void setDataSource(DataSource dataSource) {
userUpdate = new UserUpdate(dataSource);
userQuery = new UserQuery(dataSource);
userFunction = new UserFunction(dataSource);
}
public void insert(User user) {
userUpdate.update(
new Object[] {user.getName(), user.getAge()});
}
public List allUser() {
return userQuery.execute();
}
public int count() {
return userFunction.run();
}
}
将SQL封装重用之后,从UserDAO程序编写的角度来看,完全是用对象操作的方式来进行的,配合程序的修改,可以写个简单的程序来看看运作是否正常:
RdbmsDemo SpringDAODemo.java
package onlyfun.caterpillar;
import org.springframework.context.ApplicationContext;
import org.springframework.context.
support.ClassPathXmlApplicationContext;
import java.util.List;
public class SpringDAODemo {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext(
"beans-config.xml");
User user = new User();
user.setName("just933");
user.setAge(new Integer(26));
IUserDAO userDAO =
(IUserDAO) context.getBean("userDAO");
userDAO.insert(user);
System.out.println("笔数: " + userDAO.count());
List list = userDAO.allUser();
for(int i = 0; i < list.size(); i++) {
User next = (User) list.get(i);
System.out.println("/n/tId:/t" + next.getId());
System.out.println("/tName:/t" + next.getName());
System.out.println("/tAge:/t" + next.getAge());
}
}
}
良葛格的话匣子<<<
为什么UserDAO中的UserUpdate、UserQuery、UserFunction等不使用IoC依赖注入呢?除了简化范例实现之外,还有一个观念:不要为了IoC而IoC!每个对象不管三七二十一都要来个依赖注入的话,被注入依赖的对象上,将会有一大堆冗长的Setter方法。建议有两个以上的对象需要注入相同的依赖时,才考虑使用Spring IoC容器来进行依赖注入。
5.2.6 DataFieldMaxValueIncrementer
在插入数据至数据库中时,您会需要设置主键,主键的设置最好是与业务键值无关,在Spring中,您可以使用org.springframework.jdbc.support.incrementer.DataFieldMaxValue- Incrementer来为您产生主键值,DataFieldMaxValueIncrementer有许多针对不同数据库的实例,例如DB2SequenceMaxValueIncrementer、HsqlMaxValueIncrementer、MySQL- MaxValueIncrementer、OracleSequenceMaxValueIncrementer、PostgreSQLSequence- MaxValueIncrementer,您可以操作它的nextIntValue()、nextLongValue() 或nextStringValue() 来产生下一个主键值。
5.2.7 Spring 2.0的NamedParameterJdbcTemplate
在Spring 2.0中新增了NamedParameterJdbcTemplate类,这样您在编写JDBC的SQL陈述时就不必使用占位字符'?'了,而是使用实际的命名参数(Named parameter)来保留SQL中会变动的数据部分,例如原本您是这么编写一个查询的:
String sql = "SELECT * FROM user WHERE id=?";
List rows = jdbcTemplate.queryForList(sql,
new Object[] {id.intValue()});
现在可以使用NamedParameterJdbcTemplate改写为以下的方式:
String sql = "SELECT * FROM user WHERE id=:userId";
SqlParameterSource namedParameters =
new MapSqlParameterSource("userId", id);
NamedParameterJdbcTemplate jdbcTemplate =
new NamedParameterJdbcTemplate(dataSource);
List rows = jdbcTemplate.queryForList(sql, namedParameters);
注意到命名参数":userId"实际值的指定方式,在这里使用的是SqlParameterSource,在构造时直接指定命名参数与实际值。您也可以使用Map对象来指定多个命名参数的实际值,例如:
String sql = "INSERT INTO user (name,age) VALUES(:userName, :userAge)";
Map namedParameters = new HashMap();
namedParameters.put("userName", name);
namedParameters.put("userAge", age);
NamedParameterJdbcTemplate jdbcTemplate =
new NamedParameterJdbcTemplate(dataSource);
jdbcTemplate.update(sql, namedParameters);
您也可以将一个POJO(Plain Old Java Object)对象的值作为命名参数值的依据,例如:
String sql = "INSERT INTO user (name,age) VALUES(:name, :age)";
SqlParameterSource namedParameters =
new BeanPropertySqlParameterSource(user);
NamedParameterJdbcTemplate jdbcTemplate =
new NamedParameterJdbcTemplate(dataSource);
jdbcTemplate.update(sql, namedParameters);
其中"user"所参考的对象是JdbcTemplateDemo项目中的User实例。下面具体将JdbcTemplateDemo项目的UserDAO类改写为使用NameParameterJdbcTemplate的方式:
NamedParameterDemo UserDAO.java
package onlyfun.caterpillar;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.jdbc.core.namedparam
.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam
.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam
.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
public class UserDAO implements IUserDAO {
private NamedParameterJdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
jdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
}
public void insert(User user) {
String sql = "INSERT INTO user (name,age) VALUES(:name, :age)";
SqlParameterSource namedParameters =
new BeanPropertySqlParameterSource(user);
jdbcTemplate.update(sql, namedParameters);
}
public User find(Integer id) {
String sql = "SELECT * FROM user WHERE id=:userId";
SqlParameterSource namedParameters =
new MapSqlParameterSource("userId", id);
List rows = jdbcTemplate.queryForList(sql, namedParameters);
Iterator it = rows.iterator();
if(it.hasNext()) {
Map userMap = (Map) it.next();
Integer i = new Integer(userMap.get("id").toString());
String name = userMap.get("name").toString();
Integer age =
new Integer(userMap.get("age").toString());
User user = new User();
user.setId(i);
user.setName(name);
user.setAge(age);
return user;
}
return null;
}
}
5.2.8 Spring 2.0的SimpleJdbcTemplate
如果您使用的JDK 是5.0以上的版本,则可以利用Spring 2.0中SimpleJdbcTemplate所提供的泛型(Generic)功能,例如原来您可能是这么查询数据的:
public User find(Integer id) {
String sql = "SELECT * FROM user WHERE id=?";
RowMapper mapper = new RowMapper() {
public Object mapRow(ResultSet rs, int rowNum)
throws SQLException {
User user = new User();
user.setId(new Integer(rs.getInt("id")));
user.setName(rs.getString("name"));
user.setAge(new Integer(rs.getInt("age")));
return user;
}
};
JdbcTemplate simpleJdbcTemplate =
new JdbcTemplate(this.getDataSource());
return (User) jdbcTemplate.queryForObject(sql, mapper, id);
}
若改用SimpleJdbcTemplate,则可以如下编写:
public User find(Integer id) {
String sql = "SELECT * FROM user WHERE id=?";
ParameterizedRowMapper mapper =
new ParameterizedRowMapper () {
public User mapRow(ResultSet rs, int rowNum)
throws SQLException {
User user = new User();
user.setId(new Integer(rs.getInt("id")));
user.setName(rs.getString("name"));
user.setAge(new Integer(rs.getInt("age")));
return user;
}
};
SimpleJdbcTemplate simpleJdbcTemplate =
new SimpleJdbcTemplate(this.getDataSource());
return simpleJdbcTemplate.queryForObject(sql, mapper, id);
}
可以注意到,当中使用了泛型功能,可以直接在mapRow()方法之后传回User实例,而SimpleJdbcTemplate的queryForObject()方法也可以直接传回查询结果的User实例。下面将JdbcTemplateDemo项目的UserDAO改写为使用SimpleJdbcTemplate的方式:
SimpleJdbcTemplateDemo UserDAO.java
package onlyfun.caterpillar;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.springframework.jdbc.core.simple.ParameterizedRowMapper;
import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
public class UserDAO implements IUserDAO {
private SimpleJdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
jdbcTemplate = new SimpleJdbcTemplate(dataSource);
}
public void insert(User user) {
String sql = "INSERT INTO user (name,age) VALUES(?, ?)";
String name = user.getName();
Integer age = user.getAge();
jdbcTemplate.update(sql, new Object[] {name, age});
}
public User find(Integer id) {
String sql = "SELECT * FROM user WHERE id=?";
ParameterizedRowMapper mapper =
new ParameterizedRowMapper () {
public User mapRow(ResultSet rs, int rowNum)
throws SQLException {
User user = new User();
user.setId(new Integer(rs.getInt("id")));
user.setName(rs.getString("name"));
user.setAge(new Integer(rs.getInt("age")));
return user;
}
};
return jdbcTemplate.queryForObject(sql, mapper, id);
}
}