Mybatis一知半解

  1. mybatis核心类及接口

    1. Configuration:

      mybatis核心配置类,用于管理mybatis相关的配置信息

      当调用Configuration的getMapper方法时,会调用mapperRegistry.getMapper方法,该方法继续调用mapperProxyFactory.newInstance方法来生成一个具体的代理对象

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      public <T> T getMapper(Class<T> type, org.apache.ibatis.session.SqlSession sqlSession) {
      return mapperRegistry.getMapper(type, sqlSession);
      }

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

      protected T newInstance(MapperProxy<T> mapperProxy) {
      return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
      }

      public T newInstance(SqlSession sqlSession) {
      final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
      return newInstance(mapperProxy);
      }
    2. SqlSessionFactoryBuilder:

      mybatis初始化时,调用XMLConfigBuilder读取所有的MybatisMapConfig.xml和所有的*Mapper.xml文件,构建mybatis运行的核心对象Configuration,然后将其作为参数构建一个SqlSessionFactory对象

      其中XMLConfigBuilder在构建Configuration对象时,也会调用XMLMapperBuilder来读取*Mapper,XMLMapperBuilder会使用XMLStatementBuilder来读取和构建所有的SQL语句

      1
      2
      3
      4
      5
      6
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      return build(parser.parse());

      public SqlSessionFactory build(Configuration config) {
      return new DefaultSqlSessionFactory(config);
      }
    3. SqlSession:

      mybatis工作的主要接口,可以通过此接口执行SQL语句、获取mapper、管理事务等;SqlSession的执行,实际是通过对应Executor来进行的

      1
      2
      3
      4
      5
      public class DefaultSqlSession implements SqlSession {
      private final Configuration configuration;
      private final Executor executor;
      ......
      }
    4. SqlSessionFactory:

      创建SqlSession的工厂类接口

    5. SqlNode:

      sql节点接口,相关实现类:IfSqlNode、TrimSqlNode、ChooseSqlNode、TextSqlNode等

      1
      2
      3
      public interface SqlNode {
      boolean apply(DynamicContext context);
      }
    6. Executor:

      SqlSession的Sql执行,都是委托给Executor实现的,

      1
      2
      3
      Executor
      BaseExecutor CachingExecutor
      SimpleExecutor ReuseExecutor BatchExecutor

      其中,BaseExecutor定义了几个抽象方法,该方法的具体实现交由子类完成

      1
      2
      3
      4
      5
      BaseExecutor.java

      protected abstract int doUpdate(xxx)
      protected abstract List<BatchResult> doFlushStatements(xxx)
      protected abstract <E> List<E> doQuery(xxx)

      SimpleExecutor:每次执行update或select时,会开启一个新的Statement对象,执行完立马关闭该对象

      ReuseExecutor:每次执行update或select时,先从StatementMap(Map<String,Statement>)中根据sql查找对应的Statement对象,如果存在,直接取出使用,如果不存在,新建一个Statement对象,执行完后不关闭,而是将其放置到StatementMap中

      BatchExecutor:执行update时,如果当前要执行的sql和Statement对象和保存的sql和Statement对象不相同,将当前要执行的Statement对象放到StatementList(List)中,并保存此次执行的sql和Statement对象

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      @Override
      public int doUpdate(MappedStatement ms, Object parameterObject) throws SQLException {
      ......
      if (sql.equals(currentSql) && ms.equals(currentStatement)) {
      int last = statementList.size() - 1;
      stmt = statementList.get(last);
      applyTransactionTimeout(stmt);
      handler.parameterize(stmt);//fix Issues 322
      org.apache.ibatis.executor.BatchResult batchResult = batchResultList.get(last);
      batchResult.addParameterObject(parameterObject);
      } else {
      Connection connection = getConnection(ms.getStatementLog());
      stmt = handler.prepare(connection, transaction.getTimeout());
      handler.parameterize(stmt); //fix Issues 322
      currentSql = sql;
      currentStatement = ms;
      statementList.add(stmt);
      batchResultList.add(new org.apache.ibatis.executor.BatchResult(ms, sql, parameterObject));
      }
      ......

      // 这里没有直接通过statement对象执行SQL操作
      handler.batch(stmt);
      return BATCH_UPDATE_RETURN_VALUE;
      }

      // 真正执行SQL操作是在doFlushStatements方法中进行的,当Executor执行doQuery方法或commit方法(如SqlSession执行commit方法)时,会调用flushStatements方法
      public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException {
      ......
      batchResult.setUpdateCounts(stmt.executeBatch());
      ......
      }

      BaseExecutor.java
      public void commit(boolean required) throws SQLException {
      if (closed) {
      throw new ExecutorException("Cannot commit, transaction is already closed");
      }
      clearLocalCache();
      flushStatements();
      if (required) {
      transaction.commit();
      }
      }
  2. SqlSession、Executor、PerpetualCache层级关系

    1. SqlSession <<<<<< Executor <<<<<< PerpetualCache

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      public class DefaultSqlSession implements SqlSession {
      private final Executor executor;
      ......
      }

      public abstract class BaseExecutor implements Executor {
      protected PerpetualCache localCache;
      ......
      }

      public class PerpetualCache implements Cache {
      private final Map<Object, Object> cache = new HashMap<>();
      ......
      }
  3. mybatis缓存

    1. mybatis中的缓存功能由根接口Cache定义,整个体系采用装饰器设计模式,数据存储和缓存的基本功能由PerpetualCache永久缓存实现,然后根据一系列的装饰器对PerpetualCache类进行缓存策略等方面的控制

      1
      2
      3
      4
      5
      6
      7
      public interface Cache {
      /**
      * @return The identifier of this cache
      */
      String getId();
      ......
      }
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      FifoCache:先进先出算法,缓存回收策略

      LoggingCache:输出缓存命中的日志信息

      LruCache:最近最少使用算法,缓存回收策略

      ScheduledCache:调度缓存,负责定时清空缓存

      SerializedCache:缓存序列化和反序列化存储

      SoftCache:基于软引用实现的缓存管理策略

      SynchronizedCache:同步的缓存装饰器,用于防止多线程并发访问

      WeakCache:基于弱引用实现的缓存管理策略
    2. 一级缓存,又叫“本地缓存”,是PerpetualCache类型的永久缓存,位于Executor中,由1可知,Executor又位于SqlSession中,因此,一级缓存的生命周期和SqlSession的生命周期是一致的

    3. 二级缓存,又叫“自定义缓存”,实现了Cache接口的类都可以作为二级缓存;二级缓存根据namespace命名空间作为唯一标识,保存在Configuration核心配置对象中;二级缓存默认缓存类型为PerpetualCache,如果配置的缓存为默认类型,则mybatis会根据配置自动追加一系列装饰器

      1
      2
      3
      4
      public class Configuration {
      protected final Map<String, Cache> caches = new StrictMap<>("Caches collection");
      ......
      }

      Cache对象之间的引用顺序为:

      SynchronizedCache–>LoggingCache–>SerializedCache–>ScheduledCache–>LruCache–>PerpetualCache