转载自  mybatis源码阅读(六) ---StatementHandler了解一下

StatementHandler类结构图与接口设计

BaseStatementHandler:一个抽象类,只是实现了一些不涉及具体操作的方法

RoutingStatementHandler:类似路由器,根据配置文件来路由选择具体实现类SimpleStatementHandler、CallableStatementHandler和PreparedStatementHandler

SimpleStatementHandler:就是直接使用普通的Statement对象,这样每次执行SQL语句都需要数据库对SQL进行预编译

PrepareStatementHandler:使用PrepareStatement执行,虽然初次创建PrepareStatement时开销比较大,但在多次处理SQL时只需要初始化一次,可以有效提高性能

CallableStatementHandler:使用CallableStatement执行,CallableStatement是用来执行存储过程的。

在每个mapper节点可以设置statementType决定是否使用谁 ,如下

<!-- statementType (可选配置,默认配置为PREPARED)STATEMENT,PREPARED 或 CALLABLE 的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。 -->
<select id="findUserById" resultType="com.lpf.entity.User" statementType="PREPARED">select * from m_user where id = #{id}
</select>

1. StatementHandler

public interface StatementHandler {// 从连接中获取一个Statement对象Statement prepare(Connection connection, Integer transactionTimeout)throws SQLException;// 绑定Statement执行是所需要的参数void parameterize(Statement statement)throws SQLException;// 批量执行SQLvoid batch(Statement statement)throws SQLException;// 执行update,delete,insert语句int update(Statement statement)throws SQLException;// 执行select语句<E> List<E> query(Statement statement, ResultHandler resultHandler)throws SQLException;<E> Cursor<E> queryCursor(Statement statement)throws SQLException;BoundSql getBoundSql();// 获取封装的ParameterterHandler对象ParameterHandler getParameterHandler();}

2. BaseStatementHandler

BaseStatementHandler是一个抽象类,和Executor一样使用了模板设计方法。代码如下:

public abstract class BaseStatementHandler implements StatementHandler {protected final Configuration configuration;protected final ObjectFactory objectFactory;protected final TypeHandlerRegistry typeHandlerRegistry;//将结果映射成结果对象protected final ResultSetHandler resultSetHandler;// 使用传入的实参替换SQL语句中的?protected final ParameterHandler parameterHandler;// 记录执行sql的executorprotected final Executor executor;protected final MappedStatement mappedStatement;// 分页用到protected final RowBounds rowBounds;protected BoundSql boundSql;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);}@Overridepublic BoundSql getBoundSql() {return boundSql;}@Overridepublic ParameterHandler getParameterHandler() {return parameterHandler;}@Overridepublic Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {ErrorContext.instance().sql(boundSql.getSql());Statement statement = null;try {// 初始化Statement对象 instantiateStatement由具体的子类对象实现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);}}protected abstract Statement instantiateStatement(Connection connection) throws SQLException;protected void setStatementTimeout(Statement stmt, Integer transactionTimeout) throws SQLException {Integer queryTimeout = null;if (mappedStatement.getTimeout() != null) {queryTimeout = mappedStatement.getTimeout();} else if (configuration.getDefaultStatementTimeout() != null) {queryTimeout = configuration.getDefaultStatementTimeout();}if (queryTimeout != null) {stmt.setQueryTimeout(queryTimeout);}StatementUtil.applyTransactionTimeout(stmt, queryTimeout, transactionTimeout);}protected void setFetchSize(Statement stmt) throws SQLException {Integer fetchSize = mappedStatement.getFetchSize();if (fetchSize != null) {stmt.setFetchSize(fetchSize);return;}Integer defaultFetchSize = configuration.getDefaultFetchSize();if (defaultFetchSize != null) {stmt.setFetchSize(defaultFetchSize);}}protected void closeStatement(Statement statement) {try {if (statement != null) {statement.close();}} catch (SQLException e) {//ignore}}protected void generateKeys(Object parameter) {KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();ErrorContext.instance().store();keyGenerator.processBefore(executor, mappedStatement, null, parameter);ErrorContext.instance().recall();}}

2.1 ParameterHandler

在BaseStatementHandler中有一个对象叫ParameterHandler是用来设置参数规则的,当StatementHandler调用prepare方法之后,接下来就是调用它来进行设置参数。

public interface ParameterHandler {Object getParameterObject();void setParameters(PreparedStatement ps)throws SQLException;}

getParameterObject是用来获取参数的,setParameters(PreparedStatement ps)是用来设置参数的,相当于对sql中所有的参数都执行ps.setXXX(value);

ParameterHandler的默认实现类是DefaultParameterHandler,其实现了接口中定义的两个方法。

getParameterObject是获取参数,这个参数值就是你传递进来的值,可能是个实体、map或单个基本类型数据。

@Override
public void setParameters(PreparedStatement ps) {ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());// 去除SQL中的参数映射列表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 {// 获取对象中相应的属性值或查找map对象中的值MetaObject metaObject = configuration.newMetaObject(parameterObject);value = metaObject.getValue(propertyName);}//获取参数值对应的jdbc类型TypeHandler typeHandler = parameterMapping.getTypeHandler();JdbcType jdbcType = parameterMapping.getJdbcType();if (value == null && jdbcType == null) {jdbcType = configuration.getJdbcTypeForNull();}try {//设置参数值和jdbc类型的对应关系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);}}}}
}

3. RoutingStatementHandler

/*** RoutingStatementHandler的主要功能就是根据mapper文件中的Statement的配置,生成一个对应的StatementHandler* 可以是 SimpleStatementHandler,PreparedStatementHandler,CallableStatementHandler* 此类中的所有方法都是通过调用delegate对象的对应方法实现的*/
public class RoutingStatementHandler implements StatementHandler {private final StatementHandler delegate;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());}}
}

4. SimpleStatementHandler

SimpleStatementHandler就是使用基本的Statement来执行query、batch、update等操作,其实现还是比较简单的,SQL语句中是没有占位符的,所以相应 的paramterize()方法是空实现。

public class SimpleStatementHandler extends BaseStatementHandler {public SimpleStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);}@Overridepublic 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 {statement.execute(sql);rows = statement.getUpdateCount();}return rows;}@Overridepublic void batch(Statement statement) throws SQLException {String sql = boundSql.getSql();statement.addBatch(sql);}@Overridepublic <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {String sql = boundSql.getSql();statement.execute(sql);return resultSetHandler.<E>handleResultSets(statement);}@Overridepublic <E> Cursor<E> queryCursor(Statement statement) throws SQLException {String sql = boundSql.getSql();statement.execute(sql);return resultSetHandler.<E>handleCursorResultSets(statement);}@Overrideprotected Statement instantiateStatement(Connection connection) throws SQLException {if (mappedStatement.getResultSetType() != null) {return connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);} else {return connection.createStatement();}}@Overridepublic void parameterize(Statement statement) throws SQLException {// N/A}}

5. PreparedStatementHandler

PreparedStatementHandler底层依赖PreparedStatement对象来完成数据库的相关操作,在调用parameterize()方法完成SQL语句的参数绑定,代码也比较简单。

public class PreparedStatementHandler extends BaseStatementHandler {public PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);}@Overridepublic int update(Statement statement) throws SQLException {PreparedStatement ps = (PreparedStatement) statement;ps.execute();int rows = ps.getUpdateCount();Object parameterObject = boundSql.getParameterObject();KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);return rows;}@Overridepublic void batch(Statement statement) throws SQLException {PreparedStatement ps = (PreparedStatement) statement;ps.addBatch();}@Overridepublic <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {PreparedStatement ps = (PreparedStatement) statement;ps.execute();return resultSetHandler.<E> handleResultSets(ps);}@Overridepublic <E> Cursor<E> queryCursor(Statement statement) throws SQLException {PreparedStatement ps = (PreparedStatement) statement;ps.execute();return resultSetHandler.<E> handleCursorResultSets(ps);}@Overrideprotected 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);}}@Overridepublic void parameterize(Statement statement) throws SQLException {parameterHandler.setParameters((PreparedStatement) statement);}}

6. CallableStatementHandler

CallableStatementHandler实际就是使用CallableStatement来执行SQL语句,当然它执行的是存储过程。

public class CallableStatementHandler extends BaseStatementHandler {public CallableStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);}@Overridepublic int update(Statement statement) throws SQLException {//用来调用存储过程,它提供了对输出和输入/输出参数的支持CallableStatement cs = (CallableStatement) statement;cs.execute();int rows = cs.getUpdateCount();Object parameterObject = boundSql.getParameterObject();KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();keyGenerator.processAfter(executor, mappedStatement, cs, parameterObject);resultSetHandler.handleOutputParameters(cs);return rows;}@Overridepublic void batch(Statement statement) throws SQLException {CallableStatement cs = (CallableStatement) statement;cs.addBatch();}@Overridepublic <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {CallableStatement cs = (CallableStatement) statement;cs.execute();List<E> resultList = resultSetHandler.<E>handleResultSets(cs);resultSetHandler.handleOutputParameters(cs);return resultList;}@Overridepublic <E> Cursor<E> queryCursor(Statement statement) throws SQLException {CallableStatement cs = (CallableStatement) statement;cs.execute();Cursor<E> resultList = resultSetHandler.<E>handleCursorResultSets(cs);resultSetHandler.handleOutputParameters(cs);return resultList;}@Overrideprotected 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);}}@Overridepublic void parameterize(Statement statement) throws SQLException {//注册out参数registerOutputParameters((CallableStatement) statement);parameterHandler.setParameters((CallableStatement) statement);}private void registerOutputParameters(CallableStatement cs) throws SQLException {List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();for (int i = 0, n = parameterMappings.size(); i < n; i++) {ParameterMapping parameterMapping = parameterMappings.get(i);//处理存储过程的INOUT和OUT  if (parameterMapping.getMode() == ParameterMode.OUT || parameterMapping.getMode() == ParameterMode.INOUT) {if (null == parameterMapping.getJdbcType()) {throw new ExecutorException("The JDBC Type must be specified for output parameter.  Parameter: " + parameterMapping.getProperty());} else {if (parameterMapping.getNumericScale() != null && (parameterMapping.getJdbcType() == JdbcType.NUMERIC || parameterMapping.getJdbcType() == JdbcType.DECIMAL)) {cs.registerOutParameter(i + 1, parameterMapping.getJdbcType().TYPE_CODE, parameterMapping.getNumericScale());} else {if (parameterMapping.getJdbcTypeName() == null) {cs.registerOutParameter(i + 1, parameterMapping.getJdbcType().TYPE_CODE);} else {cs.registerOutParameter(i + 1, parameterMapping.getJdbcType().TYPE_CODE, parameterMapping.getJdbcTypeName());}}}}}}}

mybatis源码阅读(六) ---StatementHandler了解一下相关推荐

  1. mybatis源码阅读(八) ---Interceptor了解一下

    转载自  mybatis源码阅读(八) ---Interceptor了解一下 1 Intercetor MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用.默认情况下,MyBatis允许 ...

  2. mybatis源码阅读(五) ---执行器Executor

    转载自  mybatis源码阅读(五) ---执行器Executor 1. Executor接口设计与类结构图 public interface Executor {ResultHandler NO_ ...

  3. mybatis源码阅读(二):mybatis初始化上

    转载自  mybatis源码阅读(二):mybatis初始化上 1.初始化入口 //Mybatis 通过SqlSessionFactory获取SqlSession, 然后才能通过SqlSession与 ...

  4. Mybatis源码阅读之二——模板方法模式与Executor

    [系列目录] Mybatis源码阅读之一--工厂模式与SqlSessionFactory 文章目录 一. 模板方法模式 二. 同步回调与匿名函数 三. Executor BaseExecutor与其子 ...

  5. mybatis源码阅读(七) ---ResultSetHandler了解一下

    转载自  mybatis源码阅读(七) ---ResultSetHandler了解一下 1.MetaObject MetaObject用于反射创建对象.反射从对象中获取属性值.反射给对象设置属性值,参 ...

  6. mybatis源码阅读(四):mapper(dao)实例化

    转载自   mybatis源码阅读(四):mapper(dao)实例化 在开始分析之前,先来了解一下这个模块中的核心组件之间的关系,如图: 1.MapperRegistry&MapperPro ...

  7. mybatis源码阅读(三):mybatis初始化(下)mapper解析

    转载自 mybatis源码阅读(三):mybatis初始化(下)mapper解析 MyBatis 的真正强大在于它的映射语句,也是它的魔力所在.由于它的异常强大,映射器的 XML 文件就显得相对简单. ...

  8. mybatis源码阅读(一):SqlSession和SqlSessionFactory

    转载自  mybatis源码阅读(一):SqlSession和SqlSessionFactory 一.接口定义 听名字就知道这里使用了工厂方法模式,SqlSessionFactory负责创建SqlSe ...

  9. Mybatis源码阅读(一):Mybatis初始化1.1 解析properties、settings

    *************************************优雅的分割线 ********************************** 分享一波:程序员赚外快-必看的巅峰干货 如 ...

最新文章

  1. 动态规划|最大k乘积问题(C语言)
  2. Exynos4412 Uboot 移植(六)—— 相关知识补充
  3. 你的目的是什么是谁指使你_电视剧《谁说我结不了婚》第25-27集剧情:魏书帮程璐搞定投资人...
  4. Jsoup的简易使用示例
  5. 一道JS面试题目引发的思考
  6. Theano at a Glance
  7. IOS笔记 : addChildViewController
  8. 【操作系统】存储模型(二):虚拟存储技术和置换算法
  9. PostgreSQL下载安装
  10. word2016去除回车符和换行符的方法
  11. MIPS-单周期CPU设计
  12. Android Zip 解压与进度 实现,kotlin命令行输入值
  13. 元旦贺卡 html5,元旦贺卡:最牛的元旦祝福语短信
  14. 阿里无影云电脑磁盘性能测试
  15. ant+JMeter接口自动化测试框架实践
  16. KYC功能介绍:为客户提供新的机会
  17. R语言机器学习篇——决策树
  18. 专业相关的计算机知识领域,计算机技术(领域)
  19. rndc: connect failed: 127.0.0.1#953: connection refused
  20. 编译原理 - 三大经典书籍(龙书 虎书 鲸书)

热门文章

  1. csdn上修改字体的颜色
  2. 算法设计与分析——递归与分治策略——棋盘覆盖
  3. 后端学习 - JVM(上)内存与垃圾回收
  4. 《C++ Primer》7.5.1节练习
  5. SpringApplication.run做了哪些事情
  6. 二级c语言作答文件不存在,全国计算机等级考试二级C语言上机考试题库及答案...
  7. 三调 图斑地类面积_三调对于最小上图图斑面积的要求是:
  8. 操作系统内存管理--简单、页式、段式、段页式
  9. P4316 绿豆蛙的归宿 期望dp + DAG
  10. P1446 [HNOI2008]Cards