MyBatis知识Part3(SqlSession、Executor)

SqlSession

映射器通过动态代理进入到了MapperMethod的execute方法,然后通过判断进入了SqlSession的delete、update、insert、select等方法

SqlSession通过Executor、StatementHandler、ParameterHandler和ResultSetHandler来完成数据库操作和结果返回的
	Executor执行器,由它调度StatementHandler、ParameterHandler、ResultSetHandler等来执行对应的SQL。其中StatementHandler最重要
	StatementHandler的作用是使用数据库的Statement(PreparedStatement)执行操作,它是四大对象的核心,起到承上启下的作用,许多重要的插件都是通过拦截它来实现的。
	ParameterHandler用来处理SQL参数
	ResultSetHandler是进行数据集(ResultSet)的封装返回处理的,它相当复杂,不常用

Executor

SqlSession是一个门面,真正干活的是执行器,它是一个真正执行Java和数据库交互的对象,很重要。MyBatis中有3种执行器,可以在MyBatis的配置文件中选择
	SIMPLE——简易执行器,没什么特别的,不配置就是默认执行器
	REUSE——能够执行重用预处理语句的执行器
	BATCH——执行器重用语句和批量更新,批量专用的执行器
执行器提供了查询(query)方法、更新(update)方法和相关的事务方法,和其他框架并不不同。

创建Executor,在Configuration类中
package org.apache.ibatis.session;
public class Configuration {
	public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
	    executorType = executorType == null ? defaultExecutorType : executorType;
	    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
	    Executor executor;
	    if (ExecutorType.BATCH == executorType) {
	      executor = new BatchExecutor(this, transaction);
	    } else if (ExecutorType.REUSE == executorType) {
	      executor = new ReuseExecutor(this, transaction);
	    } else {
	      executor = new SimpleExecutor(this, transaction);
	    }
	    if (cacheEnabled) {
	      executor = new CachingExecutor(executor);
	    }
	    executor = (Executor) interceptorChain.pluginAll(executor);
	    return executor;
	  }
}
MyBatis将根据配置类型去确定需要创建哪一种Executor,它的缓存则用CachingExecutor进行包装Executor。在运用插件时,拦截Executor就有可能获取这样的一个对象了,在创建对象之后,回去执行这样的一行代码。
		interceptorChain.pluginAll(executor);
这就是MyBatis的插件,将构建一层层的动态代理对象 ,我们可以修改在调度真实的Executor方法之前执行配置插件的代码,这就是插件的原理。

SimpleExecutor执行器的query方法
package org.apache.ibatis.executor;
public class SimpleExecutor extends BaseExecutor {
  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();
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.<E>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);
    handler.parameterize(stmt);
    return stmt;
  }
}

MyBatis根据Configuration来构建StatementHandler,然后使用prepareStatement方法,对SQL编译和参数进行初始化。实现:调用了StatementHandler的prepare()进行了预编译和基础的设置,然后通过StatementHandler的parametersize()来设置参数,最后使用StatementHandler的query方法,把ResultHandler传递进去,使用它组织结果返回给调用者完成一次查询,现在主要看StatementHandler。

StatementHandler——数据库会话器
	数据库会话器专门处理数据库会话的。
	生成StatementHandler
package org.apache.ibatis.session;
public class Configuration {
	  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;
  }
显然创建的真实对象是一个RoutingStatementHandler对象,它实现了接口StatementHandler,和Executor一样,用代理对象做一层层的封装
RoutingStatementHandler不是真实的服务对象,它通过适配模式来找到对应的StatementHandler来执行。在MyBatis中,和Executor一样,RoutingStatementHandler分为3种:SimpleStatementHandler、PreparedStatementHandler、CallableStatementHandler。所对应的是JDBC的Statement、PreparedStatement(预编译处理)和CallableStatement(存储过程处理)
在初始化RoutingStatementHandler对象时,它会根据上下文环境决定创建哪个具体的StatementHandler对象实例。
package org.apache.ibatis.executor.statement;
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());
    }

  }
}

它定义了一个对象的配适器——delegate,是一个StatementHandler接口对象,然后构造方法根据配置来配适对应的StatementHandler对象。它的作用是给3个接口对象的使用提供一个统一且简易的配适器。此为对象的配适,可以使用对象配适器来尽可能地使用已有的类对外提供服务,可以根据需要对外屏蔽或者提供服务,甚至是加入新的朋友。

Executor执行查询时会执行StatementHandler的prepare、parameterize和query方法。
prepare方法
package org.apache.ibatis.executor.statement;
public abstract class BaseStatementHandler implements StatementHandler {
  public Statement prepare(Connection connection) throws SQLException {
    ErrorContext.instance().sql(boundSql.getSql());
    Statement statement = null;
    try {
      statement = instantiateStatement(connection);
      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);
    }
  }
}
instantiateStatement()方法对SQL进行了预编译,然后做一些基础配置,比如超时、获取的最大的行数等的设置。Executor中会调用它的parameterize()方法去设置参数Executor会调用StatementHandler 的parameterize()方法去设置参数
  public void parameterize(Statement statement) throws SQLException {
    parameterHandler.setParameters((PreparedStatement) statement);
  }
显然是调用ParameterHandler去完成的。下面看如何实现查询
执行SQL返回结果
package org.apache.ibatis.executor.statement;
public class PreparedStatementHandler extends BaseStatementHandler {
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    return resultSetHandler.<E> handleResultSets(ps);
  } 
 在执行前参数和SQL都被prepare()方法预编译,参数在parameterize()方法中已经进行了设置,所以只要执行SQL,然后返回结果就可以了。执行之后我们看到了ResultSetHandler对结果的封装和返回。
 
 一条查询SQL的执行过程:Executor先调用StatementHandler的prepare()方法预编译SQL,同时设置一些基本运行的参数。然后用parameterize()方法启用ParameterHandler设置参数,完成预编译,执行查询,update()也是如此。如果是查询,MyBatis会使用ResultSetHandler封装结果返回给调用者。

ParameterHandler——参数处理器

MyBatis通过ParameterHandler对预编译语句进行参数设置,作用是完成对预编译参数的设置。
package org.apache.ibatis.executor.parameter;
public interface ParameterHandler {
  Object getParameterObject();
  void setParameters(PreparedStatement ps)
      throws SQLException;
}
	getParameterObject()方法的作用是返回参数对象
	setParameters()方法的作用是设置预编译SQL语句的参数。
	
MyBatis为ParameterHandler提供了一个实现类DefaultParameterHandler,
setParameters的实现
package org.apache.ibatis.scripting.defaults;
public class DefaultParameterHandler implements ParameterHandler {
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) {
          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();
          typeHandler.setParameter(ps, i + 1, value, jdbcType);
        }
      }
    }
  }
}
它还是从parameterObject对象中取到参数,然后使用typeHandler转换参数,如果有设置,那么它会根据签名注册的typeHandler对参数进行处理。而typeHandler也是在MyBatis初始化时,注册在Configuration里面的,需要时就可以拿来用了。

ResultSetHandler——结果处理器

ResultSetHandler组装结果返回
ResultSetHandler的接口定义:
package org.apache.ibatis.executor.resultset;
public interface ResultSetHandler {
  <E> List<E> handleResultSets(Statement stmt) throws SQLException;
  void handleOutputParameters(CallableStatement cs) throws SQLException;
}
handleOutputParameters()方法是处理存储过程输出 参数的,handleResultSets是包装结果集的。MyBatis提供了一个DefaultResultSetHandler的实现类,在默认情况下都是通过这个类进行处理的。涉及使用Javassist(或者CGLIB)作为延迟加载,然后通过typeHandler和ObjectFactory进行组装结果再返回。改变的几率不高。

SqlSession内部运行
在这里插入图片描述

SqlSession是通过执行器Executor调度StatementHandler来运行的。StatementHandler经过3步
	prepared预编译SQl
	parameterize 设置参数
	query/update执行SQL
parameterize调用parameterHandler的方法设置,而 参数是根据类型处理器typeHandler处理的。query/update方法通过ResultSetHandler进行处理结果的封装,如果是update语句,就返回整数,封装就通过typeHandler处理结果类型,然后用ObjectFactory提供的规则组装对象,返回给调用者。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值