mybatis源碼分析-selectOne-01

源碼分析環(huán)境搭建贱案,我們將從mybatis 的原生api 開始分析头岔,作為分析源碼的入口。

1. 環(huán)境搭建

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() 方法 兩個參數(shù):
第一個:com.example.bootmybatis.dao.UserMapper.selectByPrimaryKey 對應mapper 接口中的方法诬垂。
第二個: 表示 查詢參數(shù)劲室,也就是查詢 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);
..... 省略其他方法结窘。
}

點進去我們就會發(fā)現(xiàn) SqlSession 接口申明了 這些方法很洋,我們來看一下接口的實現(xiàn)有哪些:

SqlSession.png

上圖我們sqlSession看到有三個 實現(xiàn)類,那我們在這里用的哪一個呢隧枫?最簡單的辦法就 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(); 這句話就會調(diào)這里,
  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);
// 創(chuàng)建 DefaultSqlSession 對象斤讥,這里就解釋了為什么是DefaultSqlSession  而不是其他的實現(xiàn)類纱皆。
      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 方法。

在知道實現(xiàn)類以后我們就來看一下 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 中獲取用執(zhí)行的sql 語句,其實不僅僅是這樣的铛楣。
// statement = com.example.bootmybatis.dao.UserMapper.selectByPrimaryKey
      MappedStatement ms = configuration.getMappedStatement(statement);
// 執(zhí)行查詢近迁,查詢動作交給了 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) 執(zhí)行查詢簸州,返回結(jié)果鉴竭,后面我們在來分析 executor 的過程。
我們來騷味 總結(jié)一下 岸浑,到這里我們都分析出哪些東西:
1:我們知道 sqlSession 具體接口對象 是 DefaultSqlSession
2:具體的 查詢?nèi)蝿?sql 語句執(zhí)行 是 交給了一個 叫 executor 的對象搏存。
3:用到的設計模式:工作模式 SqlSessionFactory

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市矢洲,隨后出現(xiàn)的幾起案子璧眠,更是在濱河造成了極大的恐慌,老刑警劉巖读虏,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件责静,死亡現(xiàn)場離奇詭異,居然都是意外死亡盖桥,警方通過查閱死者的電腦和手機灾螃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來葱轩,“玉大人睦焕,你說我怎么就攤上這事⊙ス埃” “怎么了垃喊?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長袜炕。 經(jīng)常有香客問我本谜,道長,這世上最難降的妖魔是什么偎窘? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任乌助,我火速辦了婚禮溜在,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘他托。我一直安慰自己掖肋,他們只是感情好,可當我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布赏参。 她就那樣靜靜地躺著志笼,像睡著了一般。 火紅的嫁衣襯著肌膚如雪把篓。 梳的紋絲不亂的頭發(fā)上纫溃,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天,我揣著相機與錄音韧掩,去河邊找鬼紊浩。 笑死,一個胖子當著我的面吹牛疗锐,可吹牛的內(nèi)容都是我干的坊谁。 我是一名探鬼主播,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼滑臊,長吁一口氣:“原來是場噩夢啊……” “哼呜袁!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起简珠,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎虹钮,沒想到半個月后聋庵,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡芙粱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年祭玉,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片春畔。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡脱货,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出律姨,到底是詐尸還是另有隱情振峻,我是刑警寧澤,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布择份,位于F島的核電站扣孟,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏荣赶。R本人自食惡果不足惜凤价,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一鸽斟、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧利诺,春花似錦富蓄、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至氛改,卻和暖如春帐萎,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背胜卤。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工疆导, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人葛躏。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓澈段,卻偏偏與公主長得像,于是被迫代替她去往敵國和親舰攒。 傳聞我的和親對象是個殘疾皇子败富,可洞房花燭夜當晚...
    茶點故事閱讀 45,685評論 2 360

推薦閱讀更多精彩內(nèi)容