欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-07-12 22:37:59
...

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的类图

mybatis源码学习------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接口类图如下
mybatis源码学习------StatementHandler

从其类图中可以看出,该接口只有一个实现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);
  }

}