MyBatis sql执行过程如下图所示:
SqlSession将执行过程委托给Executor,Executor又将执行过程交给StatementHandler具体执行。
下面我们对StatementHandler的设计进行具体分析。
SimpleStatementHandler:用于处理Statement对象的数据库操作
PreparedStatementHandler:用于处理PreparedStatement对象的数据库操作
CallableStatementHandler:用于处理CallableStatement对象的数据库操作(存储过程)
RoutingStatementHandler:用于创建上面三种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对象进行数据库操作的相关算法定义。由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不支持?形式的参数占位符,所以其parameterize方法时空的:
public void parameterize(Statement statement) throws SQLException {
// N/A
}
PreparedStatementHandler
PreparedStatementHandler定义了生成一个PrepareStatement对象,和对PrepareStatement对象进行参数填充的方法。
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 {
//将参数填充委托给parameterHandler进行处理
parameterHandler.setParameters((PreparedStatement) statement);
}
CallableStatementHandler
CallableStatementHandler定义了生成一个CallableStatement对象,和对CallableStatement对象进行参数填充的方法。
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的参数进行填充和处理。MyBatis提供了一个默认实现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的创建过程
Executor每次执行select & update,都会创建一个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) {
//根据配置的类型创建不同的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参数可以在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类型创建相应的上述StatementHandler类型