mybatis源码学习------StatementHandler

StatementHandler接口

StatementHandler接口提供了很多功能(从接口的定义中可以看出),是sql执行器接口的基础。

接口定义

public interface StatementHandler {
  //从连接对象中获取一个Statement
  Statement prepare(Connection connection, Integer transactionTimeout)
      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;
  //执行查询,返回一个游标
  <E> Cursor<E> queryCursor(Statement statement)
      throws SQLException;
  //获取绑定的BoundSql对象
  BoundSql getBoundSql();
  //获取ParameterHandler的实例
  ParameterHandler getParameterHandler();

}

StatementHandler的类图

在这里插入图片描述

RoutingStatementHandler

RoutingStatementHandler实现了StatementHandler接口,使用了策略设计模式实现,帮调用者屏蔽了不同实现的差异,简化调用。

在RoutingStatementHandler的构造函数中对delegate属性进行了初始化,其他方法的调用都是通过委派者完成的。

public class RoutingStatementHandler implements StatementHandler {
  //持有一个StatementHandler的具体实现
  private final StatementHandler delegate;

  //构造函数根据MappedStatement的类型为其创建不同的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());
    }

  }

  @Override
  public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
    return delegate.prepare(connection, transactionTimeout);
  }

  @Override
  public void parameterize(Statement statement) throws SQLException {
    delegate.parameterize(statement);
  }

  @Override
  public void batch(Statement statement) throws SQLException {
    delegate.batch(statement);
  }

  @Override
  public int update(Statement statement) throws SQLException {
    return delegate.update(statement);
  }

  @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    return delegate.query(statement, resultHandler);
  }

  @Override
  public <E> Cursor<E> queryCursor(Statement statement) throws SQLException {
    return delegate.queryCursor(statement);
  }

  @Override
  public BoundSql getBoundSql() {
    return delegate.getBoundSql();
  }

  @Override
  public ParameterHandler getParameterHandler() {
    return delegate.getParameterHandler();
  }
}

BaseStatementHandler

BaseStatementHandler及其子类使用了模板设计模式,模板设计模式有以下优点:

  1. 它封装了不变部分,扩展可变部分。它把认为是不变部分的算法封装到父类中实现,而把可变部分算法由子类继承实现,便于子类继续扩展。
  2. 在父类中提取了公共的部分代码,便于代码复用。
  3. 分方法是由子类实现的,因此子类可以通过扩展方式增加相应的功能,符合开闭原则。

属性

//全局配置对象
protected final Configuration configuration;
//默认对象工厂
protected final ObjectFactory objectFactory;
//类型处理器注册中心
protected final TypeHandlerRegistry typeHandlerRegistry;
//结果集处理器
protected final ResultSetHandler resultSetHandler;
//参数处理器
protected final ParameterHandler parameterHandler;
//sql执行器
protected 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();
  //boundSql为空表示当前为修改类操作,即增删改操作,则先生成对应的主键再创建BoundSql对象
  if (boundSql == null) {
    generateKeys(parameterObject);
    boundSql = mappedStatement.getBoundSql(parameterObject);
  }

  this.boundSql = boundSql;
  //创建ParameterHandler对象
  this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
  //创建ResultSetHandler对象
  this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
}

接口方法

prepare

代码中有设置fetchSize属性的逻辑,关于Statement的fetchSize属性可以参考这篇文章学习:https://blog.csdn.net/seven_3306/article/details/9303879

@Override
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);
    //设置statement的FetchSize属性 
    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);
  }
}

getBoundSql

@Override
public BoundSql getBoundSql() {
  return boundSql;
}

getParameterHandler

@Override
public ParameterHandler getParameterHandler() {
  return parameterHandler;
}

instantiateStatement

抽象方法,交由具体子类实现

protected abstract Statement instantiateStatement(Connection connection) throws SQLException;

setStatementTimeout

用于设置查询超时时间。如果sql语句的配置中设置了查询超时时间,则以sql语句的配置中设置的为准,否则以全局配置的查询超时时间为准。如果上面两个地方都没有设置查询超时时间,则以事务的超时时间为兜底方案。具体代码如下:

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

setFetchSize

设置FetchSize属性,如果sql语句的配置中设置了fetchSize的大小,则以sql语句的配置中设置的为准,否则以全局配置的fetchSize大小为准。

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

closeStatement

关闭statement,JDBC的模板代码

protected void closeStatement(Statement statement) {
  try {
    if (statement != null) {
      statement.close();
    }
  } catch (SQLException e) {
    //ignore
  }
}

generateKeys

生成自增主键

protected void generateKeys(Object parameter) {
  KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
  ErrorContext.instance().store();
  keyGenerator.processBefore(executor, mappedStatement, null, parameter);
  ErrorContext.instance().recall();
}

ParameterHandler接口

ParameterHandler接口提供了为sql语句绑定实参的功能

public interface ParameterHandler {
  //获取实参对象
  Object getParameterObject();
  //为sql语句绑定实参
  void setParameters(PreparedStatement ps) throws SQLException;
}

ParameterHandler接口类图如下
在这里插入图片描述

从其类图中可以看出,该接口只有一个实现DefaultParameterHandler,其代码如下:

DefaultParameterHandler

属性

//类型处理器注册中心
private final TypeHandlerRegistry typeHandlerRegistry;
private final MappedStatement mappedStatement;
//实参对象
private final Object parameterObject;
private final BoundSql boundSql;
//全局配置对象
private final Configuration configuration;

构造函数

public DefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
  this.mappedStatement = mappedStatement;
  this.configuration = mappedStatement.getConfiguration();
  this.typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();
  this.parameterObject = parameterObject;
  this.boundSql = boundSql;
}

接口方法

getParameterObject

获取实参对象

@Override
public Object getParameterObject() {
  return parameterObject;
}
setParameters

通过TypeHandler为sql语句绑定实参。

@Override
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) {//OUT表示出参,这里只处理入参
        Object value;
        String propertyName = parameterMapping.getProperty();
        if (boundSql.hasAdditionalParameter(propertyName)) {//属性存在于additionalParameters属性中(可以通过<bind/>传入)
          value = boundSql.getAdditionalParameter(propertyName);
        } else if (parameterObject == null) {//实参为空
          value = null;
        } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {//可以转换为对应的JDBC类型
          value = parameterObject;
        } else {//其余情况直接将其包装为MetaObject对象
          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实现为sql语句绑定实参
          typeHandler.setParameter(ps, i + 1, value, jdbcType);
        } catch (TypeException | SQLException e) {
          throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
        }
      }
    }
  }
}

Statement和PreparedStatement的区别

1、Statement用于执行静态SQL语句,在执行时,必须指定一个事先准备好的SQL语句。
2、PrepareStatement是预编译的SQL语句对象,sql语句被预编译并保存在对象中。被封装的sql语句代表某一类操作,语句中可以包含动态参数“?”,在执行时可以为“?”动态设置参数值。
3、使用PrepareStatement对象执行sql时,sql被数据库进行解析和编译,然后被放到命令缓冲区,每当执行同一个PrepareStatement对象时,它就会被解析一次,但不会被再次编译。在缓冲区可以发现预编译的命令,并且可以重用。
4、PrepareStatement可以减少编译次数提高数据库性能。

SimpleStatementHandler

继承自抽象类BaseStatementHandler,底层使用JDBC的Statement对象来完成数据库操作。

update

@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) {
    //执行sql
    statement.execute(sql, Statement.RETURN_GENERATED_KEYS);
    //获取受影响的行数
    rows = statement.getUpdateCount();
    //执行后处理逻辑,将自增主键保存到入参中
    keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);
  } else if (keyGenerator instanceof SelectKeyGenerator) {//通过<selectKey>配置的主键生成器
    //执行sql
    statement.execute(sql);
    //获取受影响的行数
    rows = statement.getUpdateCount();
    //执行后处理逻辑
    keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);
  } else {//没有配置主键生成器
    statement.execute(sql);
    rows = statement.getUpdateCount();
  }
  return rows;
}

其余方法

因为SimpleStatementHandler底层使用的是Statement完成对数据库的操作,所以parameterize方法是一个空实现。

其中ResultSetType枚举类的意思如下:

枚举值含义
ResultSet.TYPE_FORWARD_ONLY默认的cursor 类型,仅仅支持结果集forward ,不支持backforward ,random ,last ,first 等操作
ResultSet.TYPE_SCROLL_INSENSITIVE支持结果集backforward ,random ,last ,first 等操作,对其它session 对数据库中数据做出的更改是不敏感的。
ResultSet.TYPE_SCROLL_SENSITIVE支持结果集backforward ,random ,last ,first 等操作,对其它session 对数据库中数据做出的更改是敏感的,即其他session 修改了数据库中的数据,会反应到本结果集中。
ResultSet.DEFAULT默认类型,依赖不同的驱动
@Override
public void batch(Statement statement) throws SQLException {
  String sql = boundSql.getSql();
  statement.addBatch(sql);
}

@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
  String sql = boundSql.getSql();
  statement.execute(sql);
  //通过ResultSetHandler组装结果
  return resultSetHandler.handleResultSets(statement);
}

@Override
public <E> Cursor<E> queryCursor(Statement statement) throws SQLException {
  String sql = boundSql.getSql();
  statement.execute(sql);
  //通过ResultSetHandler组装结果
  return resultSetHandler.handleCursorResultSets(statement);
}

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

@Override
public void parameterize(Statement statement) {
  // N/A
}

PreparedStatementHandler

继承自抽象类BaseStatementHandler,底层使用JDBC的PreparedStatement完成对数据库的操作。实现逻辑与SimpleStatementHandler类似,看看就ok。

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

  @Override
  public int update(Statement statement) throws SQLException {
    //预编译语句
    PreparedStatement ps = (PreparedStatement) statement;
    //执行sql
    ps.execute();
    //受影响的行数
    int rows = ps.getUpdateCount();
    //获取实参
    Object parameterObject = boundSql.getParameterObject();
    //获取主键生成器
    KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
    //后置处理逻辑
    keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
    return rows;
  }

  @Override
  public void batch(Statement statement) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.addBatch();
  }

  @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    return resultSetHandler.handleResultSets(ps);
  }

  @Override
  public <E> Cursor<E> queryCursor(Statement statement) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    return resultSetHandler.handleCursorResultSets(ps);
  }
  //底层通过JDBC的Connection完成PrepareStatement的创建
  @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);
    }
  }

  @Override
  public void parameterize(Statement statement) throws SQLException {
    //通过parameterHandler完成实参的绑定
    parameterHandler.setParameters((PreparedStatement) statement);
  }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值