MyBatis 原理浅析 3 ——数据操作

前言

在前文《MyBatis 原理浅析——基本原理》一文中简单分析了 MyBatis 的实现原理,MyBatis 的数据库操作是通过 Executor 执行的。Executor 是一个接口,有三个实现类,分别是 SimpleExecutor、ReuseExecutor 和 BatchExecutor。

查询数据的流程

查询数据是通过 SqlSession 的方法实现的,SqlSession 封装了 Executor 的相关操作。以 select 为例,首先根据 SQL 语句关联的 statement 从 configuration 中获取 MappedStatement 对象,然后调用 Executor 的 query 方法执行查询操作。statement 的格式是命名空间+ID,ID 即是 XML 中 select 标签的 id 属性。MappedStatement 对象存储了 XML 中的 SQL 配置,如 ParameterMap、ResultMap 等。

 

@Override
public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
    try {
      MappedStatement ms = configuration.getMappedStatement(statement);
      executor.query(ms, wrapCollection(parameter), rowBounds, handler);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
}

 

 默认情况下,使用 Executor 接口的 SimpleExecutor 实现。SimpleExecutor 继承了抽象类 BaseExecutor。BaseExecutor 实现了 Executor 接口的方法,实现了缓存机制,可以从缓存中直接返回查询结果,但更具体的数据库操作交给子类 SimpleExecutor 实现。在 SimpleExecutor  类中执行查询的 doQuery 方法如下所示。首先从 Configuration 中创建 StatementHandler 接口实例,默认使用 RoutingStatementHandler 实现。然后在 prepareStatement 方法中获取数据库连接、初始化 Statement、设置参数等。再调用 StatementHandler 的 query 方法,执行数据查询,查询结果交给 resultHandler 处理。最后关闭 Statement。

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

在 RoutingStatementHandler 中会根据配置选择一个 StatementHandler 的实现用于进一步的数据处理,默认是 PreparedStatementHandler 类。在该类的 query 方法中会执行 Statement 数据查询请求,请求完成后调用 ResultSetHandler 对查询结果进行处理和封装。ResultSetHandler 接口定义了对返回结果集进行处理的方法,默认使用的实现类是 DefaultResultSetHandler,在 handleResultSets 方法中完成结果集到 Java Bean 的映射。

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

 

查询结果的缓存

MyBatis 支持对查询结果进行缓存,以减少数据库操作提高效率,默认情况下执行 insert、update、delete 等操作会清理缓存。执行查询操作前,首先创建 CacheKey 对象,根据 SQL 语句、id、offset、limit 和参数更新 CacheKey 对象,CacheKey 对象会记录这些数据并更新 hashcode、checksum、count 几个整数,这些数据都用于判断 CacheKey 对象是否相等。

public void update(Object object) {
    int baseHashCode = object == null ? 1 : ArrayUtil.hashCode(object); 
    count++;
    checksum += baseHashCode;
    baseHashCode *= count;
    hashcode = multiplier * hashcode + baseHashCode;
    updateList.add(object);
}

  

select 语句的查询结果以 Map 形式存放在 PerpetualCache 类中,Map 的 key 是 CacheKey 对象,Map 的 value 是查询结果。

更新数据的流程

DefaultSqlSession 的 insert、delete 操作都是复用 update 操作实现的,update 的操作流程与 select 类似,不做过多阐述。所不同的是,执行 update 操作前需要先清理缓存,在 update 操作完成以后,还需要返回受影响的行数和 KeyGenerator 配置的自增值。

事务提交与回滚

DefaultSqlSession 的 commit 和 rollback 操作都是在 BaseExecutor 中实现的,先清除缓存,然后清理 Statement,如果有需要再调用 Transaction 的相关方法。如果要求强制执行,或者不自动提交且有脏数据,就会执行 Transaction 的 commit 或 rollback 操作。

每周 3 篇学习笔记或技术总结,面向有一定基础的 Java 程序员,内容涉及 Java 进阶、虚拟机、MySQL、NoSQL、分布式计算、开源框架等多个领域。关注作者或微信公众号 backend-develop 第一时间获取最新内容。

MyBatis 原理浅析 3 ——数据操作

相关推荐