美文网首页mybatis
mybatis源码分析-selectOne-01

mybatis源码分析-selectOne-01

作者: 愤怒的奶牛 | 来源:发表于2019-10-07 00:28 被阅读0次

    源码分析环境搭建,我们将从mybatis 的原生api 开始分析,作为分析源码的入口。

    1. 环境搭建

    github: https://github.com/yoxiyoxiiii/boot-mybatis

    2. 查询分析 (mapper.xml 可用逆向工程生成,重点不是这个)
    @Test
        public void selectOne() throws IOException {
            String config = "config.xml";
            InputStream inputStream = Resources.getResourceAsStream(config);
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
                    .build(inputStream);
            SqlSession sqlSession = sqlSessionFactory.openSession();
            User user = sqlSession.selectOne("com.example.bootmybatis.dao.UserMapper.selectByPrimaryKey", 1);
            System.out.println(user);
            sqlSession.close();
        }
    

    这里基本上的每句代码都有作用,我们姑且先从第一感觉 selectOne() 这个方法开始。

    User user = sqlSession.selectOne("com.example.bootmybatis.dao.UserMapper.selectByPrimaryKey", 1);
    

    sqlSession.selectOne() 方法 两个参数:
    第一个:com.example.bootmybatis.dao.UserMapper.selectByPrimaryKey 对应mapper 接口中的方法。
    第二个: 表示 查询参数,也就是查询 id = 1 的user 对象。

    2.1 sqlSession.selectOne()
    public interface SqlSession extends Closeable {
    
      /**
       * Retrieve a single row mapped from the statement key.
       * @param <T> the returned object type
       * @param statement
       * @return Mapped object
       */
      <T> T selectOne(String statement);
    
      /**
       * Retrieve a single row mapped from the statement key and parameter.
       * @param <T> the returned object type
       * @param statement Unique identifier matching the statement to use.
       * @param parameter A parameter object to pass to the statement.
       * @return Mapped object
       */
      <T> T selectOne(String statement, Object parameter);
    
      /**
       * Retrieve a list of mapped objects from the statement key and parameter.
       * @param <E> the returned list element type
       * @param statement Unique identifier matching the statement to use.
       * @return List of mapped object
       */
      <E> List<E> selectList(String statement);
    
      /**
       * Retrieve a list of mapped objects from the statement key and parameter.
       * @param <E> the returned list element type
       * @param statement Unique identifier matching the statement to use.
       * @param parameter A parameter object to pass to the statement.
       * @return List of mapped object
       */
      <E> List<E> selectList(String statement, Object parameter);
    ..... 省略其他方法。
    }
    

    点进去我们就会发现 SqlSession 接口申明了 这些方法,我们来看一下接口的实现有哪些:

    SqlSession.png

    上图我们sqlSession看到有三个 实现类,那我们在这里用的哪一个呢?最简单的办法就 debug 一下,可以看出来是 DefaultSqlSession ,然后我们就来看看 为什么是 DefaultSqlSession 而不是其他。这里就要从前面几句代码找答案了。

            String config = "config.xml";
            InputStream inputStream = Resources.getResourceAsStream(config); //这里是读取配置文件
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
                    .build(inputStream);// 这句话就是关键
            SqlSession sqlSession = sqlSessionFactory.openSession();
    
    2.2: SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    public class SqlSessionFactoryBuilder {
    
      public SqlSessionFactory build(Reader reader) {
        return build(reader, null, null);
      }
    
      public SqlSessionFactory build(Reader reader, String environment) {
        return build(reader, environment, null);
      }
    
      public SqlSessionFactory build(Reader reader, Properties properties) {
        return build(reader, null, properties);
      }
    
      public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
        try {
          XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
          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(InputStream inputStream) {
        return build(inputStream, null, null);
      }
    
      public SqlSessionFactory build(InputStream inputStream, String environment) {
        return build(inputStream, environment, null);
      }
    
      public SqlSessionFactory build(InputStream inputStream, Properties properties) {
        return build(inputStream, null, properties);
      }
      public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        try {
          XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
          return build(parser.parse());
        } catch (Exception e) {
          throw ExceptionFactory.wrapException("Error building SqlSession.", e);
        } finally {
          ErrorContext.instance().reset();
          try {
            inputStream.close();
          } catch (IOException e) {
            // Intentionally ignore. Prefer previous error.
          }
        }
      }
    // 一层层往下,最后会到这里
      public SqlSessionFactory build(Configuration config) {
        return new DefaultSqlSessionFactory(config);
      }
    
    
      public SqlSessionFactory build(Configuration config) {
        return new DefaultSqlSessionFactory(config);
      }
    
    public class DefaultSqlSessionFactory implements SqlSessionFactory {
    
      private final Configuration configuration;
    
      public DefaultSqlSessionFactory(Configuration configuration) {
        this.configuration = configuration;
      }
    
      @Override
      public SqlSession openSession() {
        return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
      }
    
      @Override
      public SqlSession openSession(boolean autoCommit) {
        return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, autoCommit);
      }
    
      @Override
      public SqlSession openSession(ExecutorType execType) {
        return openSessionFromDataSource(execType, null, false);
      }
    
      @Override
      public SqlSession openSession(TransactionIsolationLevel level) {
        return openSessionFromDataSource(configuration.getDefaultExecutorType(), level, false);
      }
    
      @Override
      public SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level) {
        return openSessionFromDataSource(execType, level, false);
      }
    
      @Override
      public SqlSession openSession(ExecutorType execType, boolean autoCommit) {
        return openSessionFromDataSource(execType, null, autoCommit);
      }
    
      @Override
      public SqlSession openSession(Connection connection) {
        return openSessionFromConnection(configuration.getDefaultExecutorType(), connection);
      }
    
      @Override
      public SqlSession openSession(ExecutorType execType, Connection connection) {
        return openSessionFromConnection(execType, connection);
      }
    
      @Override
      public Configuration getConfiguration() {
        return configuration;
      }
    //  SqlSession sqlSession = sqlSessionFactory.openSession(); 这句话就会调这里,
      private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
        Transaction tx = null;
        try {
          final Environment environment = configuration.getEnvironment();
          final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
          tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
          final Executor executor = configuration.newExecutor(tx, execType);
    // 创建 DefaultSqlSession 对象,这里就解释了为什么是DefaultSqlSession  而不是其他的实现类。
          return new DefaultSqlSession(configuration, executor, autoCommit);
        } 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.3 回到 selectOne 方法。

    在知道实现类以后我们就来看一下 DefaultSqlSession 中的 selectOne() 方法:

    @Override
      public <T> T selectOne(String statement, Object parameter) {
        // Popular vote was to return null on 0 results and throw exception on too many. 复用 selectList()方法。
        List<T> list = this.selectList(statement, parameter);
        if (list.size() == 1) { //判断集合,返回一条
          return list.get(0);
        } else if (list.size() > 1) {
          throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
        } else {
          return null;
        }
      }
    
    @Override
      public <E> List<E> selectList(String statement, Object parameter) {
        return this.selectList(statement, parameter, RowBounds.DEFAULT);
      }
    //最后会到这里
     @Override
      public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
        try {// 从配置信息里面获取 sql 映射的对象 MappedStatement  暂时可以暴力的理解为 从 mapper.xml 中获取用执行的sql 语句,其实不仅仅是这样的。
    // statement = com.example.bootmybatis.dao.UserMapper.selectByPrimaryKey
          MappedStatement ms = configuration.getMappedStatement(statement);
    // 执行查询,查询动作交给了 executor 对象。
          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();
        }
      }
    

    executor.query(ms, wrapCollection(parameter) 执行查询,返回结果,后面我们在来分析 executor 的过程。
    我们来骚味 总结一下 ,到这里我们都分析出哪些东西:
    1:我们知道 sqlSession 具体接口对象 是 DefaultSqlSession
    2:具体的 查询任务 sql 语句执行 是 交给了一个 叫 executor 的对象。
    3:用到的设计模式:工作模式 SqlSessionFactory

    相关文章

      网友评论

        本文标题:mybatis源码分析-selectOne-01

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