Skip to content

Latest commit

 

History

History
497 lines (357 loc) · 16.9 KB

2.MyBatis体系结构与工作原理.md

File metadata and controls

497 lines (357 loc) · 16.9 KB

MyBatis体系结构与工作原理

Mybatis的工作流程

image-20220226164152028

1.解析配置文件

2.创建工厂类

3.创建会话

4.会话操作数据库

jar包结构

image-20220226164605234

Mybatis的架构分层

1.提供给应用使用:接口层

2.处理数据库操作:核心层

3.支持工作:基础层

image-20220226164841501

Mybatis缓存

image-20220226172013267

image-20220226172036057

所有的缓存实现类总体上可分为三类:基本缓存、淘汰算法缓存、装饰器缓存。

image-20220226172152255

image-20220226172205917

一级缓存工作位置与维护对象

一级缓存是基于sqlsession的缓存,会话是无法共享的。不同的一级会话对数据修改会产生脏数据。

image-20220226171208971

二级缓存

作用域:namespace

二级缓存应该工作在一级缓存之前。

二级缓存工作位置与维护对象

image-20220226174041517

开启二级缓存的方法

第一步:在mybatis-config.xml文件配置

<setting name="cacheEnabled" value="true"/>

第二步:在 Mapper.xml 中配置标签

<!-- 声明这个 namespace 使用二级缓存 -->
<cache type="org.apache.ibatis.cache.impl.PerpetualCache"
    size="1024" <!--最多缓存对象个数,默认 1024-->
    eviction="LRU"  <!--回收策略-->
    flushInterval="120000" <!--自动刷新时间 ms,未配置时只有调用时刷新-->
    readOnly="false"/> <!--默认是 false(安全),改为 true 可读写时,对象必须支持序列化 -->

Cache属性讲解

image-20220226174542289

image-20220226174557541

二级缓存的关闭

我们可以在单个 Statement ID 上显式关闭二级缓存(默认是 true)

<select id="selectBlog" resultMap="BaseResultMap" useCache="false>

Mybatis源码解析

二级缓存的key是什么?怎么命中缓存?

参数+mapper

XMLConfigBuilder

private void parseConfiguration(XNode root) {
    try {
      //issue #117 read properties first
      propertiesElement(root.evalNode("properties"));
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      loadCustomVfs(settings);
      loadCustomLogImpl(settings);
      typeAliasesElement(root.evalNode("typeAliases"));
      pluginElement(root.evalNode("plugins"));
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      settingsElement(settings);
      // read it after objectFactory and objectWrapperFactory issue #631
      environmentsElement(root.evalNode("environments"));
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      typeHandlerElement(root.evalNode("typeHandlers"));
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

配置解析

01-SQLSessionFactory

会话创建

创建 Transactio

创建 Executor

Executor 的基本类型有三种:SIMPLE、BATCH、REUSE,默认是 SIMPLE (settingsElement()读取默认值),他们都继承了抽象类 BaseExecutor。

public abstract class BaseExecutor implements Executor {
    protected abstract int doUpdate(MappedStatement ms, Object parameter)
      throws SQLException;

  	protected abstract List<BatchResult> doFlushStatements(boolean isRollback)
      throws SQLException;

  	protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
      throws SQLException;

  	protected abstract <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql)
      throws SQLException;
}

image-20220226212718711

问题:三种类型的区别(通过 update()方法对比)?

SimpleExecutor:每执行一次 update 或 select,就开启一个 Statement 对象,用 完立刻关闭 Statement 对象。

ReuseExecutor:执行 update 或 select,以 sql 作为 key 查找 Statement 对象, 存在就使用,不存在就创建,用完后,不关闭 Statement 对象,而是放置于 Map 内, 供下一次使用。简言之,就是重复使用 Statement 对象。

BatchExecutor:执行 update(没有 select,JDBC 批处理不支持 select),将所 有 sql 都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存 了多个 Statement 对象,每个 Statement 对象都是 addBatch()完毕后,等待逐一执行 executeBatch()批处理。与 JDBC 批处理相同。

02-DefaultSqlSession

创建会话的过程,我们获得了一个 DefaultSqlSession,里面包含了一个 Executor,它是 SQL 的执行者。

获取Mapper对象

  public <T> T getMapper(Class<T> type) {
    return configuration.getMapper(type, this);
  }
  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
  }
  @SuppressWarnings("unchecked")
  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }
  public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }
  protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

JDK动态代理

image-20220226213424768

Mybatis代理

image-20220226213445109

03-getMapper

执行SQL

1、MapperProxy.invoke()

// 获取缓存,保存了方法签名和接口方法的关系
final MapperMethod mapperMethod = cachedMapperMethod(method);

2、MapperMethod.execute()

image-20220226223032759

3、DefaultSqlSession.selectOne()

DefaultSqlSession.java
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);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
}

image-20220226223131437

4、BaseExecutor.query()

1)创建 CacheKey

从 Configuration 中获取 MappedStatement, 然后从 BoundSql 中获取 SQL 信 息,创建 CacheKey。这个 CacheKey 就是缓存的 Key。 然后再调用另一个 query()方法

// CachingExecutor.java
// 二级缓存
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
      throws SQLException {
    Cache cache = ms.getCache();
    if (cache != null) {
      flushCacheIfRequired(ms);
      if (ms.isUseCache() && resultHandler == null) {
        ensureNoOutParams(ms, boundSql);
        @SuppressWarnings("unchecked")
        List<E> list = (List<E>) tcm.getObject(cache, key);
        if (list == null) {
          list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          tcm.putObject(cache, key, list); // issue #578 and #116
        }
        return list;
      }
    }
    return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }
// BaseExecutor.java
// 一级缓存
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
      clearLocalCache();
    }
    List<E> list;
    try {
      queryStack++;
      list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
      if (list != null) {
        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
      } else {
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
      }
    } finally {
      queryStack--;
    }
    if (queryStack == 0) {
      for (DeferredLoad deferredLoad : deferredLoads) {
        deferredLoad.load();
      }
      // issue #601
      deferredLoads.clear();
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        // issue #482
        clearLocalCache();
      }
    }
    return list;
  }
// BaseExector.java
// 一级缓存 get put  
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    List<E> list;
    localCache.putObject(key, EXECUTION_PLACEHOLDER);
    try {
      list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    } finally {
      localCache.removeObject(key);
    }
    localCache.putObject(key, list);
    if (ms.getStatementType() == StatementType.CALLABLE) {
      localOutputParameterCache.putObject(key, parameter);
    }
    return list;
  }
// SimpleExector.java
// 一级缓存
  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.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

2)清空本地缓存

queryStack 用于记录查询栈,防止递归查询重复处理缓存。 flushCache=true 的时候,会先清理本地缓存(一级缓存):clearLocalCache(); 如果没有缓存,会从数据库查询:queryFromDatabase() 如果 LocalCacheScope == STATEMENT,会清理本地缓存。

3)从数据库查询

​ a)缓存 先在缓存用占位符占位。执行查询后,移除占位符,放入数据。

​ b)查询 执行 Executor 的 doQuery();默认是 SimpleExecutor

5、SimpleExecutor.doQuery()

1)创建 StatementHandle

在 configuration.newStatementHandler()中,new 一个 StatementHandler,先 得到 RoutingStatementHandler。

RoutingStatementHandler 里 面 没 有 任 何 的 实 现 , 是 用 来 创 建 基 本 的 StatementHandler 的。这里会根据 MappedStatement 里面的 statementType 决定 StatementHandler 的 类 型 。 默 认 是 PREPARED ( STATEMENT 、 PREPARED 、 CALLABLE)

// RoutingStatementHandler.java  
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());
    }

  }
// BaseStatementHandler.java  
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();

    if (boundSql == null) { // issue #435, get the key before calculating the statement
      generateKeys(parameterObject);
      boundSql = mappedStatement.getBoundSql(parameterObject);
    }

    this.boundSql = boundSql;

    this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
    this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
  }

2)创建 Statement

3)执行的 StatementHandler 的 query()方法

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

4)执行 PreparedStatement 的 execute()方法

5)ResultSetHandler 处理结果集

// DefaultResultSetHandler.java
public List<Object> handleResultSets(Statement stmt) throws SQLException {
    ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

    final List<Object> multipleResults = new ArrayList<>();

    int resultSetCount = 0;
    ResultSetWrapper rsw = getFirstResultSet(stmt);

    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    validateResultMapsCount(rsw, resultMapCount);
    while (rsw != null && resultMapCount > resultSetCount) {
      ResultMap resultMap = resultMaps.get(resultSetCount);
      handleResultSet(rsw, resultMap, multipleResults, null);
      rsw = getNextResultSet(stmt);
      cleanUpAfterHandlingResultSet();
      resultSetCount++;
    }

    String[] resultSets = mappedStatement.getResultSets();
    if (resultSets != null) {
      while (rsw != null && resultSetCount < resultSets.length) {
        ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
        if (parentMapping != null) {
          String nestedResultMapId = parentMapping.getNestedResultMapId();
          ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
          handleResultSet(rsw, resultMap, null, parentMapping);
        }
        rsw = getNextResultSet(stmt);
        cleanUpAfterHandlingResultSet();
        resultSetCount++;
      }
    }

    return collapseSingleResultList(multipleResults);
  }

04-MapperProxy

总结

image-20220226230005316