对spring JdbcTemplate 代码的一些理解

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等函数就好理解了。基本上就是上面三种基本形式的再封装以及相关接口的实例化实现。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值