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