1. 程式人生 > >對spring JdbcTemplate 程式碼的一些理解

對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等函式就好理解了。基本上就是上面三種基本形式的再封裝以及相關介面的例項化實現。