Mybatis源码解析05-关键组件StatementHandler

从名字可以知道,StatementHandler主要是用来帮忙处理Statement的,java.sql.Statement是用来执行SQL并返回结果的对象,StatementHandler提供了几个基础的方法:[prepare、parameterize、batch、update、query]可以看一下基代码:

public interface StatementHandler { 
  Statement prepare(Connection connection, Integer transactionTimeout)  
      throws SQLException;  
  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;  
  <E> Cursor<E> queryCursor(Statement statement)  
      throws SQLException;  
  BoundSql getBoundSql();  
  ParameterHandler getParameterHandler();  
}

参考JDBC关键部分简单梳理可以知道Statement是执行的关键,而上面StatementHandler的每一个都关乎SQL的执行。有准备Statement的方法,有设置参数的接口,有执行查询更新的接口,也有获取ParameterHandler(参数处理器的接口)
在Executor的执行过程中可以看到有以下步骤:

Connection connection = getConnection(ms.getStatementLog());  
stmt = handler.prepare(connection, transaction.getTimeout());

即先通过MapperStatement拿到Connection再调用StatementHandler的prepare方法来准备Statement。
我们可以看一下,StatementHandler的类图如下:
image.png
可以看到StatementHandler接口的实现类有两个,一个BaseStatementHandler是一个抽象类,另外一个RoutingStatementHandler从其名字大概可以知道这个Handler可能是起了一个类似路由的作用,一看其源码也确实如此:

public class RoutingStatementHandler implements StatementHandler {  

  // 可以看到这里是代理模式的运用,真正执行的是delegate
  private final StatementHandler delegate;  
  
  public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {  
	// 根据StatementType来判断创建不同的Handler
    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());  
    }  
  
  }
...
}

而StatementHandler是从哪儿来的呢,在Executor的执行过程中会通过调用Configuration#newStatementHandler方法创建一个新的StatementHandler,正是在创建StatementHandler的时候会根据不同的类型得到不同的Handler

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;  
	}
}

而BaseStatementHandler有一个抽象方法instantiateStatement需要其子类实现:

public abstract class BaseStatementHandler implements StatementHandler {
	@Override  
	public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {  
	  ErrorContext.instance().sql(boundSql.getSql());  
	  Statement statement = null;  
	  try {  
	    // 需要子类去实现,类似于是一个模板方法
	    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;
}

BaseStatementHandler一共有三个子类:
SimpleStatementHandler
CallableStatementHandler
PreparedStatementHandler
三个方法分别对应
Statement
CallableStatement
PreparedStatement
可以看一下这三个Handler的instantiateStatement方法可以

public class SimpleStatementHandler extends BaseStatementHandler {
	@Override  
	protected Statement instantiateStatement(Connection connection) throws SQLException {  
	  if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {  
	    return connection.createStatement();  
	  } else {  
	    return connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);  
	  }  
	}
}

public class PreparedStatementHandler extends BaseStatementHandler {
	@Override  
	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() == ResultSetType.DEFAULT) {  
	    return connection.prepareStatement(sql);  
	  } else {  
	    return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);  
	  }  
	}
}

public class CallableStatementHandler extends BaseStatementHandler {
	@Override  
	protected Statement instantiateStatement(Connection connection) throws SQLException {  
	  String sql = boundSql.getSql();  
	  if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {  
	    return connection.prepareCall(sql);  
	  } else {  
	    return connection.prepareCall(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);  
	  }  
	}
}

而StatementHandler接口里面的getBoundSql方法,最终会调用到org.apache.ibatis.mapping.MappedStatement#getBoundSql,其代码如下:

// org.apache.ibatis.mapping.MappedStatement#getBoundSql
public BoundSql getBoundSql(Object parameterObject) {  
  BoundSql boundSql = sqlSource.getBoundSql(parameterObject);  
  List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();  
  if (parameterMappings == null || parameterMappings.isEmpty()) {  
    boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);  
  }  
  
  // check for nested result maps in parameter mappings (issue #30)  
  for (ParameterMapping pm : boundSql.getParameterMappings()) {  
    String rmId = pm.getResultMapId();  
    if (rmId != null) {  
      ResultMap rm = configuration.getResultMap(rmId);  
      if (rm != null) {  
        hasNestedResultMaps |= rm.hasNestedResultMaps();  
      }  
    }  
  }  
  
  return boundSql;  
}

getBoundSql执行过程可以参考下图:
image.png

最后总结一下:
参考Exectuor:Mybatis源码解析04-关键组件Executor可以看到这两个核心组件的类图有相似之处,StatementHandler接口定义核心的方法,抽象类BaseStatementHandler提供一些非子类特有的行为,而子类则去实现自己特有的行为,而RoutingStatementHandler又用到了代理模式,这样让好具体的StatementHandler与上层代码解耦,上层代码面向StatementHandler接口编程即可。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值