概念
職責
ParameterHandler是用來設置參數規則的。StatementHandler中介紹到,其SimpleExecutor中調用prepare()方法之後,接下來StatementHandler就是使用parameterize來設置參數。以SampleExecutor爲例,具體代碼如下:
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection, transaction.getTimeout());
//解析並設置參數
handler.parameterize(stmt);
return stmt;
}
@Override
public void parameterize(Statement statement) throws SQLException {
parameterHandler.setParameters((PreparedStatement) statement);
}
類圖
進入源碼,該接口很簡單,且只有一個默認實現類DefaultParameterHandler
public interface ParameterHandler {
Object getParameterObject();
void setParameters(PreparedStatement ps)
throws SQLException;
}
- getParameterObject()用於讀取參數;
- setParameter():用於對PreparedStatementHandler的參數賦值;
源碼
ParameterHandler對象創建
對於ParameterHandler對象的創建過程,首先拋出結論:該對象是在創建StatementHandler對象的同時被創建完成。StatementHandler文章中我們談論到其依賴ParameterHandler和ResultSetHandler,下面我們進入正題。
上面談到了該對象在StatementHandler對象創建時被創建,所以我們從StatementHandler對象位置開始跟蹤:
- SimpleExecutor
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
// 獲取環境配置
Configuration configuration = ms.getConfiguration();
//Configuration中獲取StatementHandler,跟進去
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
- Configuration
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
//創建StatementHandler
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
跟進RoutingStatementHandler,我們以SimpleStatementHandler爲例,其餘*StatementHandler對象創建一樣,調用其父類BaseStatementHandler構造方法,所以我們跟蹤到其父類的構造方法中。
- SimpleStatementHandler
public SimpleStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
}
- BaseStatementHandler
protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
this.configuration = mappedStatement.getConfiguration();
this.executor = executor;
this.mappedStatement = mappedStatement;
this.rowBounds = rowBounds;
this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
this.objectFactory = configuration.getObjectFactory();
if (boundSql == null) { // issue #435, get the key before calculating the statement
generateKeys(parameterObject);
boundSql = mappedStatement.getBoundSql(parameterObject);
}
this.boundSql = boundSql;
// 創建參數處理器
this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
// 創建結果映射器
this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
}
從代碼註釋上,我們可以清晰看到parameterHandler 和resultSetHandler 對象的創建交給Configuration類操作;
從上面幾步源碼的跟蹤:Configuration類依次完成了StatementHandler、parameterHandler 、resultSetHandler 對象的創建過程。
那麼繼續迴歸到Configuration中。
- Configuration
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
return parameterHandler;
}
ParameterHandler解析參數
上面完成了StatementHandler,ParameterHandler和ResultSetHandler對象的創建,基本工作已準備完成,下面繼續迴歸到SimpleExecutor#prepareStatement()
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
// 獲取環境配置
Configuration configuration = ms.getConfiguration();
//Configuration中獲取StatementHandler
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
//準備Statement對象
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt);
return stmt;
}
- PrepareStatementHandler
@Override
public void parameterize(Statement statement) throws SQLException {
parameterHandler.setParameters((PreparedStatement) statement);
}
代碼裏看到是parameterHandler對象調用,上面我們瞭解到ParameterHandler接口僅有一個實現類即DefaultParameterHandler,因此Debug進去。
- DefaultParameterHandler
@Override
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
// parameterMappings 就是對 #{} 或者 ${} 裏面參數的封裝
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
// 如果是參數化的SQL,便需要循環取出並設置參數的值
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
// 如果參數類型不是 OUT ,這個類型與 CallableStatementHandler 有關
// 因爲存儲過程不存在輸出參數,所以參數不是輸出參數的時候,就需要設置。
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
// 得到#{} 中的屬性名
String propertyName = parameterMapping.getProperty();
// 如果 propertyName 是 Map 中的key
if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
// 通過key 來得到 additionalParameter 中的value值
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
// 如果不是 additionalParameters 中的key,而且傳入參數是 null, 則value 就是null
value = null;
}
// 如果 typeHandlerRegistry 中已經註冊了這個參數的 Class對象,即它是Primitive 或者是String 的話
else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
// 否則就是 Map
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
// 在通過SqlSource 的parse 方法得到parameterMappings 的具體實現中,我們會得到parameterMappings的typeHandler
TypeHandler typeHandler = parameterMapping.getTypeHandler();
// 獲取typeHandler 的jdbc type
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
}
try {
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (TypeException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
} catch (SQLException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
}
}
}
}
}
總結
總結一下ParameterHandler整體大致流程走向,以下面時序圖爲例