zoukankan      html  css  js  c++  java
  • mybatis的执行流程

    1.SqlSessionFactoryBuilder与SqlSessionFactory

      我们一般在使用mybatis是都会通过new SqlSessionFactoryBuilder.build(...)来获取SqlSessionFactory,那么这条语句发生了什么,我们来看一看源码

      (1).通过将配置文件传递给SqlSessionFactoryBuilder调用build()方法来获取SessionSessionFactory.

    public SqlSessionFactory build(Reader reader) {
        return build(reader, null, null); //我们跳到这个方法去看
    }
    public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
        try {
          XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);  //通过这个方法来加载我们mybatis的xml文件,解析成为XMLConfigBuilder对象。
          return build(parser.parse());           //我们转到这个方法
        } catch (Exception e) {
          throw ExceptionFactory.wrapException("Error building SqlSession.", e);
        } finally {
          ErrorContext.instance().reset();
          try {
            reader.close();
          } catch (IOException e) {
            // Intentionally ignore. Prefer previous error.
          }
        }
    }
    public SqlSessionFactory build(Configuration config) {
        return new DefaultSqlSessionFactory(config);    //通过将Configuration对象最为参数来新建一个DefaultSqlSessionFactory.
    }

    2.SqlSessionFactory与SqlSession

     (1)我们一般通过SqlSqlSessionFactory.oppenSession()来获取一个SqlSession.我们来看看源码都发生了什么。

    @Override
      public SqlSession openSession() {
        return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false); //我们调到这个方法,会以默认的Executor来执行我们的操作。
      }
    private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
        Transaction tx = null;
        try {
          final Environment environment = configuration.getEnvironment(); //通过Configuration对象去获取我们的配置信息。
          final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
          tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
          final Executor executor = configuration.newExecutor(tx, execType);  //新建默认的Executor,默认为Simple
          return new DefaultSqlSession(configuration, executor, autoCommit);  //将Configuration,我们得到的executor作为参数来新建一个DefaultSqlSession.
        } catch (Exception e) {
          closeTransaction(tx); // may have fetched a connection so lets call close()
          throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
        } finally {
          ErrorContext.instance().reset();
        }
      }

      (2)我们也可以通过自定义的ExecutorType来创建我们的SqlSessionFactory

    public interface SqlSessionFactory {
    
      SqlSession openSession();
    
      SqlSession openSession(boolean autoCommit);
      SqlSession openSession(Connection connection);
      SqlSession openSession(TransactionIsolationLevel level);
    
      SqlSession openSession(ExecutorType execType);
      SqlSession openSession(ExecutorType execType, boolean autoCommit);
      SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
      SqlSession openSession(ExecutorType execType, Connection connection);
    
      Configuration getConfiguration();
    
    }

      (3)SqlSession介绍

       sqlSession作为顶层的接口,为我们提供的数据库访问的接口方法。

    3.Executor

      (1)在sqlSession中实际上并没有实际的数据库操作而是交给下层的Executor来进行,Executor这一层主要负责mybatis中缓存的查询。

      举个例子:

      

    @Override
      public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
        try {
          MappedStatement ms = configuration.getMappedStatement(statement);    //获取MapperStatement对象,在mybatis中我们标签<select/>,<update/>,等这些CRUD标签都会被解析为一个个的MappedStatement对象。
          return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); //调用executor的方法来查询
        } catch (Exception e) {
          throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
        } finally {
          ErrorContext.instance().reset();
        }
      }

      (2)我们转到executor(BaseExecutor)的方法中,注意在BaseExecutor中存储的是我们的一级缓存,关于一二级缓存在下一篇中提及,此处只讨论执行过程

    @Override
      public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
        BoundSql boundSql = ms.getBoundSql(parameter);
        CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);  //查找缓存的key
        return query(ms, parameter, rowBounds, resultHandler, key, boundSql); //转到这个方法
     }
    @Override
      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;
      }
    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);   //将当前的查找key放入到缓存中
        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;
      }

    我们转到其中一个子类中的查询数据库方法(SimpleExecutor)

    @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);  //通过configuration来获取StatementHandler对象
          stmt = prepareStatement(handler, ms.getStatementLog());
          return handler.<E>query(stmt, resultHandler);  //将查询数据库的操作交给StatementHandler去实现
        } finally {
          closeStatement(stmt);
        }
      }

    4.StatementHandler

     statementHandler用来执行原始的Jdbc操作

    @Override
      public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        PreparedStatement ps = (PreparedStatement) statement;
        ps.execute();      //通过preparedStatement来执行原始的数据库操作
        return resultSetHandler.<E> handleResultSets(ps); //返回结果并封装为ResultHandler对象
      }

    这样mybatis的一次执行就完成了。

    5.最后我们在使用SqlSession时一般会使用SqlSession.getMapper来获取我们的代理类

    @Override
      public <T> T getMapper(Class<T> type) {
        return configuration.<T>getMapper(type, this); //通过configuration来获取Mapper
      }
    //configuration类中的方法
    public
    <T> T getMapper(Class<T> type, SqlSession sqlSession) { return mapperRegistry.getMapper(type, sqlSession); //在configuration对象中的mapperRegistry来获取mapper,在mapperRegistry中保存了我们的接口信息。 }
    //mapperRegistry类中的方法
    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); //通过mapperProxyFactory来获取mapper实例 } catch (Exception e) { throw new BindingException("Error getting mapper instance. Cause: " + e, e); }
    //MapperProxyFactory类中的方法
    protected
    T newInstance(MapperProxy<T> mapperProxy) { return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); //通过jdk动态代理来获取代理对象 } public T newInstance(SqlSession sqlSession) { final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); }

    通过以上来获取我们的Mapper对象。

    对于一些细节,可以去查看mybatis的源码进行学习

  • 相关阅读:
    centos安装docker-compose
    CentOS安装Docker
    【JPA】【笔记】GenerationType四中类型
    【maven】多个子项目、父项目之间的引用问题【转】
    一个页面多个echarts图表自适应
    jQuery操作单选按钮(radio)用法
    CASE WHEN .... THEN END 的用法
    遮罩层
    oracle 数据库的字段的增删改主键设定删除
    自动获取ROI-用于SFR算法的ROI获取
  • 原文地址:https://www.cnblogs.com/liwangcai/p/10738151.html
Copyright © 2011-2022 走看看