Mybatis执行流程----源码分析

Mybatis


初始化流程

//初始化        
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("config.xml"));
  • new一个构建者

    public class SqlSessionFactoryBuilder {
        public SqlSessionFactory build(Reader reader, String environment, Properties properties)
        public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties)
        public SqlSessionFactory build(Configuration config)
    }
    

    构建者提供了字节流,字符流,配置类以及他们的重载方法,用于构建session工厂

  • 工厂内使用一个XML配置构建器,解析配置文件,并返回DefaultSqlSessionFactory

    public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        try {
          XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
          //返回DefaultSqlSessionFactory
          return new DefaultSqlSessionFactory(parser.parse());
        } catch (Exception e) {
          throw ExceptionFactory.wrapException("Error building SqlSession.", e);
        } finally {
            inputStream.close();
        }
      }
    

使用

SqlSession sqlSession = sqlSessionFactory.openSession();
User o = sqlSession.selectOne("usermapper.selectOne");
  • 开启一个session

    private SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
        try {
       	  //获取环境变量
          Environment environment = configuration.getEnvironment();
          //获取事务工厂
          TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
          //返回一个链接的包装类
          Transaction tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
          //返回一个执行器
          Executor executor = configuration.newExecutor(tx, execType);
          //返回DefaultSqlSession   (配置信息,执行器,是否自动提交)
          return new DefaultSqlSession(configuration, executor, autoCommit);
        } catch (Exception e) {
          throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
        } 
      }
    
    • Transaction类是什么

      /**
      包装数据库链接,处理他们的生命周期,包括创建,提交,回滚,关闭.
      */
      public interface Transaction {
      	Connection getConnection() throws SQLException;
          void commit() throws SQLException;
          void rollback() throws SQLException;
          void close() throws SQLException;
          Integer getTimeout() throws SQLException;
      }
      
    • new Executor()做了什么

      protected boolean cacheEnabled = true;
      public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
          //确保默认为简单执行器
          executorType = executorType == null ? defaultExecutorType : executorType;
          executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
          Executor executor;
          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);
          }
          //cacheEnabled默认为true 默认为开启缓存
          if (cacheEnabled) {
            executor = new CachingExecutor(executor);
          }
          //使用拦截器对其进行处理
          executor = (Executor) interceptorChain.pluginAll(executor);
          return executor;
        }
      
  • selectOne()

    public <T> T selectOne(String statement, Object parameter) {
        List<T> list = this.selectList(statement, parameter);
        if (list.size() == 1) {
          return list.get(0);
        } else if (list.size() > 1) {
          //too many
        } else {
          return null;
        }
    }
    
    • selectOne 使用 selectList 来实现

    • 传入一个全局唯一ID

      mybatis需要知道你具体想要执行的语句,也就是我们在配置文件中配置或者注解配置的语句,这是我们和mybatis的一种约定,使用配置文件中根标签的namespace+执行语句的id,作为全局唯一的ID
      
  • selectList()

    private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
        try {
          //获取执行信息
          MappedStatement ms = configuration.getMappedStatement(statement);
          //执行查询
          return executor.query(ms, wrapCollection(parameter), rowBounds, handler);
        } catch (Exception e) {
        }
      }
    
    • 这里的执行的executor就是前面返回的CachingExecutor

        public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
          //这里是对Sql的解析,BoundSql中包含具体要执行的Sql,传入的参数,返回的参数等...
          BoundSql boundSql = ms.getBoundSql(parameterObject);
          //计算缓存的key
          CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
          //查询
          return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
        }
      
      public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
            throws SQLException {
          //从Statement获取二级缓存
          Cache cache = ms.getCache();
          if (cache != null) {
            flushCacheIfRequired(ms);
            //判断当前Statement是否开启了二级缓存
            if (ms.isUseCache() && resultHandler == null) {
              ensureNoOutParams(ms, boundSql);
              //从二级缓存获取数据
              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);
        }
      
      • 从这里可以发现mybatis如果开启了二级缓存是优先使用二级缓存中的数据的
      public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        List<E> list;
        try {
          //在一级缓存中查找
          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);
          }
        }
        return list;
      }
      
      • 从数据库中查询
      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和localCache构成
          localOutputParameterCache.putObject(key, parameter)
        }
        return list;
      }
      
      • 从缓存中查询
      private void handleLocallyCachedOutputParameters(MappedStatement ms, CacheKey key, Object parameter, BoundSql boundSql) {
        if (ms.getStatementType() == StatementType.CALLABLE) {
          //直接取返回结果的缓存
          final Object cachedParameter = localOutputParameterCache.getObject(key);
          //处理返回
          if (cachedParameter != null && parameter != null) {
          	metaParameter.setValue(parameterName, cachedValue);
          }
        }
      }
      
      • 从这里可以发现,一级缓存的实现是使用的语句执行结果进行处理返回.
      • 从前面的二级缓存代码处可以看到,二级缓存是直接使用处理后的结果.
      • 此处两个缓存不同.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值