mybatis

mybatis源码分析-selectOne-01

2019-10-07  本文已影响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

上一篇 下一篇

猜你喜欢

热点阅读