分析到这里的时候,mybatis 初始化、接口、事务、缓存等主要功能都已经讲完了,现在就还剩下 StatementHandler 这个真正干活的家伙没有分析了;所以接下来的博客内容主要和数据库的关系比较密切,而 StatementHandler 的主要流程也基本是和 JDBC 的流程是一一对应的;

一、StatementHandler 执行流程

在 mybatis 系列文章的第一篇,我放了一张 mybatis 整体的执行流程图:

从上面的图中也能比较清楚的看到 StatementHandler 的职责:获取 Statement -> 设置参数 -> 查询数据库 -> 将查询结果映射为 JavaBean,从这里也能看到是和我们使用原生 JDBC 的流程是一样的;而整个过程 StatementHandler 又将其拆分成了部分:

  • KeyGenerator:主键设置
  • ParameterHandler:参数设置
  • ResultSetHandler:结果集设置

这里我们首先介绍 StatementHandler 的类结构:

  • RoutingStatementHandler:路由处理器,这个相当于一个静态代理,根据 MappedStatement.statementType 创建对应的对处理器;
  • SimpleStatementHandler:不需要预编译的简单处理器;
  • PreparedStatementHandler:预编译的 SQL 处理器;
  • CallableStatementHandler:主要用于存储过程的调度;

这里的 SimpleStatementHandler、PreparedStatementHandler、CallableStatementHandler 同时也和 JDBC 的三个 Statement 一一对应;首先我们还是先看一下接口方法:

public interface StatementHandler {
Statement prepare(Connection connection) throws SQLException; // 获取 Statement
void parameterize(Statement statement) throws SQLException; // 参数化
void batch(Statement statement) throws SQLException; // 批处理
int update(Statement statement) throws SQLException;
<E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException;
BoundSql getBoundSql(); // 获取绑定sql
ParameterHandler getParameterHandler(); // 得到参数处理器
}

其中公共的方法都封装到了 BaseStatementHandler 中(这里使用的是模版模式);

// 获取 Statement
@Override
public Statement prepare(Connection connection) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
statement = instantiateStatement(connection); // 实例化 Statement
setStatementTimeout(statement); // 设置超时
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);
}
} // 由子类提供不同的实例化
protected abstract Statement instantiateStatement(Connection connection) throws SQLException;

二、StatementHandler 子类

1. RoutingStatementHandler

静态代理模式,根据 MappedStatement.statementType 创建对应的对处理,默认是 PREPARED,可以使用 XML 配置或者注解的方式指定;

public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
// 创建路由选择语句处理器
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()) { // 根据语句类型,委派到不同的语句处理器(STATEMENT|PREPARED|CALLABLE)
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());
}
}

2. SimpleStatementHandler

其功能和 JDBC.Statement 对应,从初始化方法也可以看到使用的是简单 Statement;

@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
// 调用 Connection.createStatement
if (mappedStatement.getResultSetType() != null) {
return connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
} else {
return connection.createStatement();
}
}
@Override
public int update(Statement statement) throws SQLException {
String sql = boundSql.getSql();
Object parameterObject = boundSql.getParameterObject();
KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
int rows;
if (keyGenerator instanceof Jdbc3KeyGenerator) {
statement.execute(sql, Statement.RETURN_GENERATED_KEYS);
rows = statement.getUpdateCount();
keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);
} else if (keyGenerator instanceof SelectKeyGenerator) {
statement.execute(sql);
rows = statement.getUpdateCount();
keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);
} else {
//如果没有keyGenerator,直接调用Statement.execute和Statement.getUpdateCount
statement.execute(sql);
rows = statement.getUpdateCount();
}
return rows;
}

这里在更新的时候需要区分 KeyGenerator,因为使用的是简单 Statement,所以需要在查询的时候指定返回主键 statement.execute(sql, Statement.RETURN_GENERATED_KEYS);

3. PreparedStatementHandler

其功能和 PreparedStatement 对应,这里初始化的时候可以看到比 SimpleStatementHandler 要复杂一些,因为 PreparedStatement 更新返回主键有三个方法,详细分析后面会单独放一篇详细讲解;

@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
//调用Connection.prepareStatement
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);
}
}

还有一点不同的就是 PreparedStatement 需要参数化设置,就是设置预编译 SQL 对应占位符的参数;

// DefaultParameterHandler
@Override
public void setParameters(PreparedStatement ps) throws SQLException {
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) {
// 如果不是OUT,才设进去
Object value;
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) {
// 若有额外的参数, 设为额外的参数
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
// 若参数为null,直接设null
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
// 若参数有相应的TypeHandler,直接设object
value = parameterObject;
} else {
// 除此以外,MetaObject.getValue反射取得值设进去
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
TypeHandler typeHandler = parameterMapping.getTypeHandler();
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
// 不同类型的set方法不同,所以委派给子类的setParameter方法
jdbcType = configuration.getJdbcTypeForNull();
}
typeHandler.setParameter(ps, i + 1, value, jdbcType);
}
}
}
}

另外还有 CallableStatementHandler ,代码也很简单,这里就不详细分析了;

最新文章

  1. 高性能 Socket 组件 HP-Socket v3.2.1 正式发布
  2. get github
  3. CooMark网页颜色取色表
  4. 在 Excel 工作簿中定义决策表(Oracle Policy Modeling-Define decision tables in Excel workbooks)
  5. U盘分区之后如何恢复
  6. Android读书笔记0-从零开始
  7. POJ 2533 Longest Ordered Subsequence 最长递增序列
  8. JS的IE和FF兼容性问题汇总
  9. Python---十年语言之首
  10. MySql查看表信息
  11. 最简单的jdbc操作
  12. 腾讯织云:DevOps 流水线应用平台践行之路
  13. JPG .vs. PNG
  14. Ubuntu下使用Sublime Text 3配置Python开发环境
  15. CodeForces755F 贪心 + 多重背包二进制优化
  16. 洛谷 P1571 眼红的Medusa【二分查找】 || 【map】
  17. Jmeter原理
  18. 查询Sql Server数据库对象结构
  19. jquery1.9 下检测浏览器类型和版本的方法
  20. sqlserver 建表语句,获取建表语句的存储过程,包括排序规则,索引,字段说明,支持同时生成多个表

热门文章

  1. 一文了解有趣的位运算(&amp;、|、^、~、&gt;&gt;、&lt;&lt;)
  2. 《VR入门系列教程》之5---应用方向
  3. linux初学者-系统启动故障篇
  4. Linux设备驱动程序学习----1.设备驱动程序简介
  5. Python基础总结之第五天开始【认识简单的条件语句,也可以叫判断语句】(新手可相互督促)
  6. 主机cpu突然飙高,如何快速排查问题
  7. 【iOS】no identity found Command /usr/bin/codesign failed with exit code 1
  8. 【iOS】Updating local specs repositories
  9. 【转载】【VSCode】Windows下VSCode编译调试c/c++
  10. Oracle创建设置查询权限用户