您的位置:首页 > 编程语言 > Java开发

对spring JdbcTemplate 代码的一些理解

2016-03-18 00:23 465 查看
Spring将数据访问过程中固定和可变的部分明确的划分为两个不同的类:模板(template)和回调(callback)。模板管理过程中固定的部分,而回调处理自定义的数据访问代码。

Spring的JDBC框架承担了资源管理和异常处理的工作,从而简化了JDBC代码,让我们只需编写从数据库读写数据的必须代码。

对于JdbcTemplate类就从query相关的函数开始看吧。

对于query函数按参数大致可以分为三类:callback参数分别为 ResultSetExtractor<T>、RowCallbackHandler、RowMapper<T>

如:

public <T> T query(
PreparedStatementCreator psc, final PreparedStatementSetter pss, final ResultSetExtractor<T> rse)
throws DataAccessException
public void query(PreparedStatementCreator psc, RowCallbackHandler rch) throws DataAccessException
public <T> List<T> query(PreparedStatementCreator psc, RowMapper<T> rowMapper)

public <T> T query(String sql, Object[] args, int[] argTypes, ResultSetExtractor<T> rse) throws DataAccessException
public void query(String sql, Object[] args, int[] argTypes, RowCallbackHandler rch) throws DataAccessException
public <T> List<T> query(String sql, Object[] args, int[] argTypes, RowMapper<T> rowMapper) throws DataAccessException




对于ResultSetExtractor<T>类型的参数,不需要再进行封装,在将前两个参数封装成PreparedStatementCreator和PreparedStatementSetter后,就会调用excute函数进行执行。

public <T> T query(
PreparedStatementCreator psc, final PreparedStatementSetter pss, final ResultSetExtractor<T> rse)
throws DataAccessException {

Assert.notNull(rse, "ResultSetExtractor must not be null");
logger.debug("Executing prepared SQL query");

return execute(psc, new PreparedStatementCallback<T>() {
public T doInPreparedStatement(PreparedStatement ps) throws SQLException {
ResultSet rs = null;
try {
if (pss != null) {
pss.setValues(ps);
}
rs = ps.executeQuery();
ResultSet rsToUse = rs;
if (nativeJdbcExtractor != null) {
rsToUse = nativeJdbcExtractor.getNativeResultSet(rs);
}
//结果查询出来后,这里直接调用ResultSetExtractor接口的extractData对Resultset进行转换
return rse.extractData(rsToUse);
}
finally {
JdbcUtils.closeResultSet(rs);
if (pss instanceof ParameterDisposer) {
((ParameterDisposer) pss).cleanupParameters();
}
}
}
});
}


从代码中可以看到,在excute函数的第二个参数中直接创建了PreparedStatementCallback的回调实例,里面首先进行结果查询,得到resultset结果集。然后调用传进来的ResultSetExtractor对结果集进行转换。

如果query的回调参数是RowCallbackHandler类型。如:

public void query(String sql, PreparedStatementSetter pss, RowCallbackHandler rch) throws DataAccessException {
//使用ResultSetExtractor接口的实现类RowCallbackHandlerResultSetExtractor将RowCallbackHandler进行封装
query(sql, pss, new RowCallbackHandlerResultSetExtractor(rch));
}
可以看到对于RowCallbackHandler使用了ResultSetExtractor接口的实现类RowCallbackHandlerResultSetExtractor将RowCallbackHandler进行封装。其中RowCallbackHandlerResultSetExtractor的具体实现如下:

private static class RowCallbackHandlerResultSetExtractor implements ResultSetExtractor<Object> {

private final RowCallbackHandler rch;

public RowCallbackHandlerResultSetExtractor(RowCallbackHandler rch) {
this.rch = rch;
}

public Object extractData(ResultSet rs) throws SQLException {
while (rs.next()) {
this.rch.processRow(rs);
}
return null;
}
}


可以看到在实现的extractData函数中对resultset结果集进行逐行便利,调用RowCallbackHandler的processRow函数进行逐行处理结果。

所以RowCallbackHandler的作用是对结果集中的一行数据进行处理。每一次调用只处理一行数据。最终的结果RowCallbackHandlerResultSetExtractor不负责保存和返回,而是由RowCallbackHandler的实现类自己负责保存。因此query函数返回值为void。

第三种RowMapper<T>回调参数类型,如:

public <T> List<T> query(String sql, Object[] args, RowMapper<T> rowMapper) throws DataAccessException {
//使用ResultSetExtractor接口的实现类RowMapperResultSetExtractor将RowMapper<T>进行封装
return query(sql, args, new RowMapperResultSetExtractor<T>(rowMapper));
}
同样是使用了ResultSetExtractor接口的实现类RowMapperResultSetExtractor对RowMapper<T>进行了封装。RowMapperResultSetExtractor的实现如下:

public class RowMapperResultSetExtractor<T> implements ResultSetExtractor<List<T>> {

private final RowMapper<T> rowMapper;

private final int rowsExpected;

/**
* Create a new RowMapperResultSetExtractor.
* @param rowMapper the RowMapper which creates an object for each row
*/
public RowMapperResultSetExtractor(RowMapper<T> rowMapper) {
this(rowMapper, 0);
}

/**
* Create a new RowMapperResultSetExtractor.
* @param rowMapper the RowMapper which creates an object for each row
* @param rowsExpected the number of expected rows
* (just used for optimized collection handling)
*/
public RowMapperResultSetExtractor(RowMapper<T> rowMapper, int rowsExpected) {
Assert.notNull(rowMapper, "RowMapper is required");
this.rowMapper = rowMapper;
this.rowsExpected = rowsExpected;
}

public List<T> extractData(ResultSet rs) throws SQLException {
List<T> results = (this.rowsExpected > 0 ? new ArrayList<T>(this.rowsExpected) : new ArrayList<T>());
int rowNum = 0;
while (rs.next()) {
//对结果集调用RowMapper<T>的mapRow函数进行逐行处理,extractData负责对所有行的处理结果进行保存。
//RowMapper<T>的mapRow函数将返回每一行的数据处理为T类型的对象实例后返回
results.add(this.rowMapper.mapRow(rs, rowNum++));
}
return results;
}

}


从代码中可以看到extractData函数调用RowMapper<T>的mapRow函数对结果集进行逐行处理,extractData负责对所有行的处理结果进行保存。RowMapper<T>的mapRow函数将返回每一行的数据处理为T类型的对象实例后返回。

这就是在使用RowMapper<T>和RowCallbackHandler上的区别。

下面再看下query函数的第一个参数,第一个参数基本上是两种类型,一个是String,一个是PreparedStatementCreator类型,都是用来表示对sql语句的封装。String类型的表示传进来的sql语句,大多数最终都会封装成PreparedStatementCreator接口类型,除了public <T> List<T> query(String sql, RowMapper<T> rowMapper)
public void query(String sql, RowCallbackHandler rch) throws DataAccessException
public <T> T query(final String sql, final ResultSetExtractor<T> rse) throws DataAccessException
在JdbcTemplate中定义了一个SimplePreparedStatementCreator内部类来实现PreparedStatementCreator接口。

/**
* Simple adapter for PreparedStatementCreator, allowing to use a plain SQL statement.
*/
private static class SimplePreparedStatementCreator implements PreparedStatementCreator, SqlProvider {

private final String sql;

public SimplePreparedStatementCreator(String sql) {
Assert.notNull(sql, "SQL must not be null");
this.sql = sql;
}

public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
return con.prepareStatement(this.sql);
}

public String getSql() {
return this.sql;
}
}

这个类很简单,基本上是将要调用的sql与PreparedStatement进行了封装。
对于query函数的另一个参数,可以看作是sql语句的赋值参数。这个参数有两种类型,一个是Object[],另一个是PreparedStatementSetter接口类型的参数。Object[]数组类型的参数会封装成ArgumentPreparedStatementSetter类。从这个接口和的名字也可以看出该类的作用。就是用来设置PreparedStatement的,用来对PreparedStatement进行赋值操作的。

**
* Simple adapter for {@link PreparedStatementSetter} that applies a given array of arguments.
*
* @author Juergen Hoeller
* @since 3.2.3
*/
public class ArgumentPreparedStatementSetter implements PreparedStatementSetter, ParameterDisposer {

private final Object[] args;

/**
* Create a new ArgPreparedStatementSetter for the given arguments.
* @param args the arguments to set
*/
public ArgumentPreparedStatementSetter(Object[] args) {
this.args = args;
}

public void setValues(PreparedStatement ps) throws SQLException {
if (this.args != null) {
for (int i = 0; i < this.args.length; i++) {
Object arg = this.args[i];
doSetValue(ps, i + 1, arg);
}
}
}

/**
* Set the value for prepared statements specified parameter index using the passed in value.
* This method can be overridden by sub-classes if needed.
* @param ps the PreparedStatement
* @param parameterPosition index of the parameter position
* @param argValue the value to set
* @throws SQLException
*/
protected void doSetValue(PreparedStatement ps, int parameterPosition, Object argValue) throws SQLException {
if (argValue instanceof SqlParameterValue) {
SqlParameterValue paramValue = (SqlParameterValue) argValue;
StatementCreatorUtils.setParameterValue(ps, parameterPosition, paramValue, paramValue.getValue());
}
else {
StatementCreatorUtils.setParameterValue(ps, parameterPosition, SqlTypeValue.TYPE_UNKNOWN, argValue);
}
}

public void cleanupParameters() {
StatementCreatorUtils.cleanupParameters(this.args);
}

}


现在理解了这几种基本的调用后,对于JdbcTemplate中实现的queryForList、queryForMap、queryForObject等函数就好理解了。基本上就是上面三种基本形式的再封装以及相关接口的实例化实现。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  spring jdbc 数据库