美文网首页
MyBatis原理

MyBatis原理

作者: 紫色红色黑色 | 来源:发表于2019-04-14 18:32 被阅读0次

    介绍

    MyBatis是优秀的ORM框架,封装JDBC。可以通过XML或者注解的方式配置SQL。

    执行原理

    • 初始化。解析默认配置文件mybatis-config.xml。将XML中的配置保存到Configration对象中。并以Configration对象为参数生成SqlSessionFactoryConfigration包括
      • mapperRegistry:MapperRegistry:该对象内部维护一个HashMap,key是<mapper>标签中的namespace接口Class,value是MapperProxyFactory对象。MapperProxyFactory作用是生成一个代理对象,下文会详细说明。
      • mappedStatements:Map<String, MappedStatement>:是一个继承HashMapStrictMap对象,key是namespace.方法ID的字符串,value是MappedStatement对象。MappedStatement封装了<select>标签级别的全部信息,包括返回值,入参,SQL。
      • caches:Map<String, Cache>:是一个继承HashMapStrictMap对象,key是namespace,value是缓存,主要作用于二级缓存。
    //XMLMapperBuilder中解析Mapper文件
    private void configurationElement(XNode context) {
      try {
        String namespace = context.getStringAttribute("namespace");
        if (namespace == null || namespace.equals("")) {
          throw new BuilderException("Mapper's namespace cannot be empty");
        }
        //设置建造器当前构造的namespace
        builderAssistant.setCurrentNamespace(namespace);
        cacheRefElement(context.evalNode("cache-ref"));
        //设置二级缓存,保存到configration中caches,key为namespace
        cacheElement(context.evalNode("cache"));
        parameterMapElement(context.evalNodes("/mapper/parameterMap"));
        resultMapElements(context.evalNodes("/mapper/resultMap"));
        sqlElement(context.evalNodes("/mapper/sql"));
        //设置增删改查,保存到configration中的mappedStatements,key为namespace.方法id
        buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
      } catch (Exception e) {
        throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
      }
    }
    
    • 查询。使用SqlSessionFactory生成一个SqlSession对象。SqlSession是对一次数据库执行动作的抽象,该对象中包含Executor实现类对象和Configration对象。
      • 使用接口Class构建代理对象,从ConfigrationMapperRegistry中取出接口Class对应的MapperProxyFactory对象。MapperProxyFactory会先创建InvocationHandler的实现类MapperProxy对象,然后使用JDK动态代理创建接口的代理对象。
      • 代理对象调用方法,被MapperProxy拦截执行其invoke方法。先以方法的全限定名字符串去ConfigrationmappedStatements中取出对应的MappedStatement对象。然后Executor执行MappedStatement,返回结果。
    //MapperRegistry中获取MapperProxyFactory
    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
      //接口Class获取MapperProxyFactory
      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);
      }
    }
    
    
    //MapperProxyFactory生成代理对象
    public T newInstance(SqlSession sqlSession) {
      //初始化InvocationHandler实现类MapperProxy
      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);
    }
    
    
    
    public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
      try {
        //接口Class.ID为key获取MappedStatement
        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();
      }
    }
    

    缓存

    MyBatis分一级缓存和二级缓存,

    • 一级缓存是SqlSession级别,又称本地缓存。
    • 二级缓存是namespace级别,又称全局缓存。
    Executor类图
    • 准备,SqlSessionFactory创建SqlSessionConfigration会创建一个Executor对象放入SqlSession中。可以根据ExecutorType创建对应的Executor对象。默认是创建SimpleExecutor对象,先调用父类BaseExecutor构造器,初始化PerpetualCache,这时PerpetualCache对象就是一级缓存。二级缓存开关cacheEnabled开启情况下,会创建SimpleExecutor的委托类CachingExecutorCachingExecutorTransactionalCacheManager是二级缓存管理器。
      • 一级缓存,只有PerpetualCache对象,
      • 二级缓存,默认情况PerpetualCache经过LruCacheSerializedCacheLoggingCacheSychronizedCache层层委托。
      • TransactionalCacheManager,内部维护名为transactionalCaches的HashMap,key就是MapperStatement中初始化的Cache,默认是SychronizedCache,value是TransactionalCache。在transactionalCaches第一次获取value时,将初始化的Cache委托给TransactionalCache
      • TransactionalCache,在内部有名为entriesToAddOnCommit的HashMapSqlSession执行一次查询SQL将结果临时保存到entriesToAddOnCommit。
    • 查询,Executor执行MappedStatement。这时Executor就是CachingExecutor对象。保存过程,先调用SimpleExecutor的query方法。查询结果保存到PerpetualCache,实现一级缓存。在SqlSession关闭时,执行commit动作,将entriesToAddOnCommit中的临时数据保存到TransactionalCache中,实现二级缓存。
    //Configration中创建executor
    public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
      executorType = executorType == null ? defaultExecutorType : executorType;
      executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
      Executor executor;
      //创建BaseExecutor实例
      if (ExecutorType.BATCH == executorType) {
        executor = new BatchExecutor(this, transaction);
      } else if (ExecutorType.REUSE == executorType) {
        executor = new ReuseExecutor(this, transaction);
      } else {
        executor = new SimpleExecutor(this, transaction);
      }
      //如果二级缓存开关开启,就委托给CachingExecutor
      if (cacheEnabled) {
        executor = new CachingExecutor(executor);
      }
      executor = (Executor) interceptorChain.pluginAll(executor);
      return executor;
    }
    
    //二级缓存
    //CachingExecutor执行查询
    public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
        throws SQLException {
      //获取初始化后的Cache
      Cache cache = ms.getCache();
      if (cache != null) {
        flushCacheIfRequired(ms);
        if (ms.isUseCache() && resultHandler == null) {
          ensureNoOutParams(ms, boundSql);
          @SuppressWarnings("unchecked")
          //到TransactionalCacheManager中TransactionalCache中查询是否有缓存好的结果
          List<E> list = (List<E>) tcm.getObject(cache, key);
          if (list == null) {
            //查询
            list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
            //先临时保存到TransactionalCache中entriesToAddOnCommit:HashMap
            tcm.putObject(cache, key, list); // issue #578 and #116
          }
          return list;
        }
      }
      return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
    }
    
    //TransactionalCacheManager中获取缓存数据
    public Object getObject(Cache cache, CacheKey key) {
      //先以初始化Cache为key获取TransactionalCache
      //然后到TransactionalCache获取数据
      return getTransactionalCache(cache).getObject(key);
    }
    //TransactionalCacheManager中初始化TransactionalCache
    private TransactionalCache getTransactionalCache(Cache cache) {
      //如果Cache对应的TransactionalCache没有,就以Cache为参数初始化
      return transactionalCaches.computeIfAbsent(cache, TransactionalCache::new);
    }
    
    
    //TransactionalCache保存查询结果
    private void flushPendingEntries() {
      //entriesToAddOnCommit数据放到Cache
      for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) {
        delegate.putObject(entry.getKey(), entry.getValue());
      }
      for (Object entry : entriesMissedInCache) {
        if (!entriesToAddOnCommit.containsKey(entry)) {
          delegate.putObject(entry, null);
        }
      }
    }
    
    
    MyBatis二级缓存默认依赖性

    resultmap

    1.一对多映射

    <resultMap>
        <collection select="嵌套查询"></collection>
    </resultMap>
    

    2.多对一映射

    <resultMap>
        <association></association>
    </resultMap>
    

    引用

    https://mybatis.org/mybatis-3/zh/sqlmap-xml.html#Result_Maps

    相关文章

      网友评论

          本文标题:MyBatis原理

          本文链接:https://www.haomeiwen.com/subject/yrbjwqtx.html