mybatis-核心处理层

Posted on By xqw

思考

BaseExecutor清空缓存,怎样才算是一个Executor,一个SqlSession一个Executor,spring里SqlSession什么时候创建?-

每个mapperInterface对应一个MapperProxyFactory,创造出的MapperProxy都会共用methodCache缓存,
而每个MapperProxy绑定一个SqlSession,每`getMapper(Class<T> type, SqlSession sqlSession)`一下就创建出一个MapperProxy,这样设计原因?-- 没有整合spring,就这样设计,实际的单例管理交给spring

一、二级缓存区别? ok
@CacheNamespace @CacheNamespaceRef使用    

JDBC源码,Prepared 如何防止参数注入攻击

访问者模式

TransactionSynchronizationManager
SpringManagedTransaction sping事务管理
ResultSetHandler
ParameterHandler

DataSource SqlSession Connection 的生命周期 -

mybatis架构图:

1. mybatis调用栈

selectByExample(example)为例,看下mybatis”主线任务”

  • 首先JDK动态代理类调用 MapperProxy(实现InvocationHandler接口)的invoke方法

  • 然后调用MapperMethod的Object execute(SqlSession sqlSession, Object[] args),之后走SqlSession对应方法 List<E> selectList(String statement, Object parameter)

  • DefaultSqlSession会从全局配置对象中找到对应的MappedStatement对象,然后给Executor接口执行query()
     public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
      try {
        MappedStatement ms = configuration.getMappedStatement(statement);
        return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
      } ...
    }
    
  • Executor 这层使用了SimpleExecutor,用CacheExecutor装饰,没有配置二级缓存直接走抽象类BaseExecutor
    List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
    

    BaseExecutor做一级缓存,然后调用SimpleExecutor的doQuery(),少了CacheKey参数,

    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
        StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
        //会调用handler.prepare() handler.parameterize()
        stmt = prepareStatement(handler, ms.getStatementLog());
        return handler.query(stmt, resultHandler);
      } finally {
        closeStatement(stmt);
      }
    }
    
  • StatementHandler Executor执行doQuery()的时候已经创建出对应的StatementHandler,由RoutingStatementHandler静态代理, 这里由参数 用PreparedStatementHandler,最后调用jdbc的java.sql.PreparedStatement执行sql语句

2. SqlSession

SqlSession主要作用

  1. 选择一个Executor策略(SIMPLE, REUSE, BATCH),将Executor封装,提供各种入参和返回值的方法

  2. 管理Session,SqlSessionManager提供一个线程绑定一个SqlSession,避免多次创建

DefaultSqlSession

    private final Configuration configuration;
    private final Executor executor; //sql执行的Executor对象,策略模式,可选择SIMPLE, REUSE, BATCH
    private final boolean autoCommit; //是否自动提交事务
    private boolean dirty; //缓存中是否有脏数据
    private List<Cursor<?>> cursorList; //游标,防止用户未关闭

DefaultSqlSession各个方法调用关系,最终会走Executor的方法

DefaultSqlSessionFactory

主要提供了两种创建DefaultSqlSession 对象的方式

SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) 

SqlSession openSessionFromConnection(ExecutorType execType, Connection connection)

SqlSessionManager

同时实现了SqlSession 接口和SqlSessionFactory

//底层封装的SqlSessionFactory 对象
private final SqlSessionFactory sqlSessionFactory;

//ThreadLocal 变量,记录一个与当前线希呈绑定的SqlSession 对象
private ThreadLocal<SqlSession> localSqlSession = new ThreadLocal<SqlSession>();

//localSqlSession 中记录的SqlSession 对象的代理对象,在SqlSessionManager初始化时,
//会使用JDK动态代理的方式为localSqlSession建代理对象
private final SqlSession sqlSessionProxy;

3. Executor

Executor 接口,中间做一堆优化操作,最终调用StatementHandler
BaseExecutor 模板方法的抽象类,封装了结果集的一级缓存,实现类三个:
1 SimpleExecutor 简单调用,每次创建都Statement
2 ReuseExecutor 缓存Statement, Map<String, Statement> statementMap字段
3 BatchExecutor 批处理sql,缓存部分sql一起发送到服务器,减少一次sql一次发送的消耗
CacheExecutor 二级缓存,装饰者,为BaseExecutor提供二级缓存

SimpleExecutor的doQuery实现:

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
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      //会调用transaction.getConnection() handler.prepare() handler.parameterize()
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

4. StatementHandler

StatementHandler接口及其实现类:

public interface StatementHandler {
    // 一般首先调用,得到Statement,供后续方法
    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(); // 绑定的sql
    ParameterHandler getParameterHandler(); // 参数处理器
}

1. RoutingStatementHandler

如名字,路由作用,对不同StatementType创建不同的策略Handler
RoutingStatementHandler主要的实现就是一个构造方法:

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

2. BaseStatementHandler

抽象类,主要提供参数绑定和 实现prepare()方法
Executor调用逻辑:先调用BaseStatement的prepare(),prepare()调用instantiateStatement()得到Statement,再调用parameterize,
最后拿Statement调query(),最后用resultSetHandler处理结果
BaseStatementHandler主要的实现:

    protected final Configuration configuration;
    protected final ObjectFactory objectFactory;
    protected final TypeHandlerRegistry typeHandlerRegistry;
    //ResultSetHandler 将结采集映射成结采对象
    protected final ResultSetHandler resultSetHandler;
    //为SQL 语句绑定实参,使用传入的实参替换SQL语句的中"?"占位符
    protected final ParameterHandler parameterHandler;
    //记录执行SQL 语句的Executor 对象
    protected final Executor executor;
    protected final MappedStatement mappedStatement;
    protected final RowBounds rowBounds;
    //RowBounds 记录了用户设置的offset 和limit ,用于在结采集中定位映射的起始位置和结束位置
    protected BoundSql boundSql; 

    public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
        ErrorContext.instance().sql(boundSql.getSql());
        Statement statement = null;
        try {
            //instantiateStatement抽象方法,字类实现
          statement = instantiateStatement(connection);
            //配置超时时间以 fetchSize
          setStatementTimeout(statement, transactionTimeout);
          setFetchSize(statement);
          return statement;
        } catch ...
    }

3. SimpleStatementHandler

SimpleStatementHandler基于java.sql.Statement接口,执行不带参数的sql语句(带参数需要自己拼装sql,有sql注入问题),主要的实现:

  @Override
  protected Statement instantiateStatement(Connection connection) throws SQLException {
    if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
      //创建普通的Statement对象
      return connection.createStatement();
    } else {
      //设置结果集是否可以滚动及其游标是否可以上下移动,设置结果集是否可更新
      return connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
    }
  }
  @Override
  //空实现
  public void parameterize(Statement statement) {
    // N/A
  }
  @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    String sql = boundSql.getSql();
    statement.execute(sql);
    return resultSetHandler.handleResultSets(statement);
  }

4.PreparedStatementHandler

PreparedStatementHandler基于java.sql.PreparedStatement,可以绑定参数接口主要实现:

  @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 {
        //在insert 语句执行完成之后,会将keyColumnNames 指定的列返回
        return connection.prepareStatement(sql, keyColumnNames);
      }
    } else if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
      //创建普通的PreparedStatement 对象
      return connection.prepareStatement(sql);
    } else {
      return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
    }
  }
  //使用parameterHandler来绑定参数
  @Override
  public void parameterize(Statement statement) throws SQLException {
    parameterHandler.setParameters((PreparedStatement) statement);
  }
  @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    return resultSetHandler.handleResultSets(ps);
  }

通过比较PreparedStatementHandler在创建Statement时绑定sqlconnection.prepareStatement(sql),执行直接ps.execute()
而SimpleStatementHandler:connection.createStatement()statement.execute(sql)
“Prepared”可以理解为准备sql,每次execute的sql一样参数可以不一样,而Simple每次execute的sql不一样

5. CallableStatementHandler

存储过程的调用,基于java.sql.CallableStatement,也会调用ParameterHandler.setParameters() 方法完成SQL语句的参数绑定,
还会调用ResultSetHandler.handleOutputParameters()处理输出参数

  //差不多
  @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);
    }
  }

  @Override
  public void parameterize(Statement statement) throws SQLException {
    //区别
    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);
      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());
            }
          }
        }
      }
    }
  }

public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    CallableStatement cs = (CallableStatement) statement;
    cs.execute();
    List<E> resultList = resultSetHandler.handleResultSets(cs);
    //区别
    resultSetHandler.handleOutputParameters(cs);
    return resultList;
  }