MyBatis sql執(zhí)行過程如下圖所示:

SqlSession將執(zhí)行過程委托給Executor,Executor又將執(zhí)行過程交給StatementHandler具體執(zhí)行。
下面我們對StatementHandler的設計進行具體分析。

SimpleStatementHandler:用于處理Statement對象的數(shù)據庫操作
PreparedStatementHandler:用于處理PreparedStatement對象的數(shù)據庫操作
CallableStatementHandler:用于處理CallableStatement對象的數(shù)據庫操作(存儲過程)
RoutingStatementHandler:用于創(chuàng)建上面三種Handler的策略類
BaseStatementHandler
在BaseStatementHandler中定義了生成Statement對象的基本算法結構,而具體生成Statement的類型和算法,則由子類自行決定:
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
//由子類自行決定要生成的Statement類型和算法
statement = instantiateStatement(connection);
setStatementTimeout(statement, transactionTimeout);
setFetchSize(statement);
return statement;
} catch (SQLException e) {
closeStatement(statement);
throw e;
} catch (Exception e) {
closeStatement(statement);
throw new ExecutorException("Error preparing statement. Cause: " + e, e);
}
}
SimpleStatementHandler
SimpleStatementHandler是利用Statement對象進行數(shù)據庫操作的相關算法定義。由instantiateStatement方法生成一個Statement對象。
protected Statement instantiateStatement(Connection connection) throws SQLException {
if (mappedStatement.getResultSetType() != null) {
return connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
} else {
return connection.createStatement();
}
}
因為Statement不支持?形式的參數(shù)占位符,所以其parameterize方法時空的:
public void parameterize(Statement statement) throws SQLException {
// N/A
}
PreparedStatementHandler
PreparedStatementHandler定義了生成一個PrepareStatement對象,和對PrepareStatement對象進行參數(shù)填充的方法。
protected Statement instantiateStatement(Connection connection) throws SQLException {
String sql = boundSql.getSql();
if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
String[] keyColumnNames = mappedStatement.getKeyColumns();
if (keyColumnNames == null) {
return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
} else {
return connection.prepareStatement(sql, keyColumnNames);
}
} else if (mappedStatement.getResultSetType() != null) {
return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
} else {
return connection.prepareStatement(sql);
}
}
public void parameterize(Statement statement) throws SQLException {
//將參數(shù)填充委托給parameterHandler進行處理
parameterHandler.setParameters((PreparedStatement) statement);
}
CallableStatementHandler
CallableStatementHandler定義了生成一個CallableStatement對象,和對CallableStatement對象進行參數(shù)填充的方法。
protected Statement instantiateStatement(Connection connection) throws SQLException {
String sql = boundSql.getSql();
if (mappedStatement.getResultSetType() != null) {
return connection.prepareCall(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
} else {
return connection.prepareCall(sql);
}
}
ParameterHandler
ParameterHandler負責對Statement的參數(shù)進行填充和處理。MyBatis提供了一個默認實現(xiàn)DefaultParameterHandler。
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
TypeHandler typeHandler = parameterMapping.getTypeHandler();
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);
}
}
}
}
}
StatementHandler的創(chuàng)建過程
Executor每次執(zhí)行select & update,都會創(chuàng)建一個StatementHandler對象。
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
//新建一個StatementHandler
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.update(stmt);
} finally {
closeStatement(stmt);
}
}
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
//根據配置的類型創(chuàng)建不同的StatementHandler
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
switch (ms.getStatementType()) {
case STATEMENT:
delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case PREPARED:
delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case CALLABLE:
delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
default:
throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
}
}
StatementType參數(shù)可以在SQL Mapper文件中進行配置:
<select id="select" resultType="mybatis.bean.User" statementType="STATEMENT">
select * from user_info where id = #{id}
</select>
StatementType有三種不同的類型:
public enum StatementType {
STATEMENT, PREPARED, CALLABLE
}
默認為PREPARED。
總結
- SimpleStatementHandler
Statement stm = conn.createStatement()
return stm.execute(sql);
- PreparedStatementHandler
PreparedStatement pstm = conn.prepareStatement(sql);
pstm.setString(1, "Hello");
return pstm.execute();
- CallableStatementHandler
CallableStatement cs = conn.prepareCall("{call pr_add(?,?,?)}");
cs.registerOutParameter(3, Types.INTEGER);
cs.setInt(1, 10);
cs.setString(2, "Hello");
cs.execute();
return cs.getInt(3);
- RoutingStatementHandler
根據mapper文件中配置的type類型創(chuàng)建相應的上述StatementHandler類型