mybatis筆記-StatementHandler

在之前已經(jīng)看過Executor和MappedStatement

mybatis筆記-Executor
mybatis筆記-MappedStatement

接下來繼續(xù)分析其內(nèi)部的流程.

1.SimpleExecutor的doQuery方法

可以看到內(nèi)部執(zhí)行的流程是由StatementHandler完成的,實(shí)際Executor并沒有真正執(zhí)行sql語句,所以我們要把關(guān)注點(diǎn)轉(zhuǎn)移到StatementHandler

  @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);
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

2.jdbc原生查詢

public static List<Map<String,Object>> queryForList(){  
    Connection connection = null;  
    ResultSet rs = null;  
    PreparedStatement stmt = null;  
    List<Map<String,Object>> resultList = new ArrayList<Map<String,Object>>();  
          
    try {  
        // 加載JDBC驅(qū)動  
        Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();  
        String url = "jdbc:oracle:thin:@localhost:1521:ORACLEDB";  
              
        String user = "trainer";   
        String password = "trainer";   
              
        // 獲取數(shù)據(jù)庫連接  
        connection = DriverManager.getConnection(url,user,password);   
              
        String sql = "select * from userinfo where user_id = ? ";  
        // 創(chuàng)建Statement對象(每一個Statement為一次數(shù)據(jù)庫執(zhí)行請求)  
        stmt = connection.prepareStatement(sql);  
              
        // 設(shè)置傳入?yún)?shù)  
        stmt.setString(1, "zhangsan");  
              
        // 執(zhí)行SQL語句  
        rs = stmt.executeQuery();  
              
        // 處理查詢結(jié)果(將查詢結(jié)果轉(zhuǎn)換成List<Map>格式)  
        ResultSetMetaData rsmd = rs.getMetaData();  
        int num = rsmd.getColumnCount();  
              
        while(rs.next()){  
            Map map = new HashMap();  
            for(int i = 0;i < num;i++){  
                String columnName = rsmd.getColumnName(i+1);  
                map.put(columnName,rs.getString(columnName));  
            }  
            resultList.add(map);  
        }  
              
    } catch (Exception e) {  
        e.printStackTrace();  
    } finally {  
        try {  
            // 關(guān)閉結(jié)果集  
            if (rs != null) {  
                rs.close();  
                rs = null;  
            }  
            // 關(guān)閉執(zhí)行  
            if (stmt != null) {  
                stmt.close();  
                stmt = null;  
            }  
            if (connection != null) {  
                connection.close();  
                connection = null;  
            }  
        } catch (SQLException e) {  
            e.printStackTrace();  
        }  
    }        
    return resultList;  
}

對比上面流程,Executor在doQuery方法中準(zhǔn)備了BoundSql,接下來還需要一下流程

  1. 獲取Connection
  2. 創(chuàng)建PreparedStatement
  3. 填充sql參數(shù)
  4. 執(zhí)行sql語句
  5. 處理查詢結(jié)果集

下來就把上面的流程走一遍

3.獲取Connection

由BaseExecutor方法提供準(zhǔn)備

  protected Connection getConnection(Log statementLog) throws SQLException {
    Connection connection = transaction.getConnection();
    if (statementLog.isDebugEnabled()) {
      return ConnectionLogger.newInstance(connection, statementLog, queryStack);
    } else {
      return connection;
    }
  }

4.創(chuàng)建PreparedStatement

  private Statement prepareStatement(StatementHandler handler, Log statementLog) 
throws SQLException {
    Statement stmt;
    Connection connection = getConnection(statementLog);
    stmt = handler.prepare(connection, transaction.getTimeout());
    handler.parameterize(stmt);
    return stmt;
  }

這部分流程是由StatementHandler的prepare方法和parameterize完成的

  1. prepare做了初始化
  2. parameterize做了參數(shù)話數(shù)據(jù)填充的工作
  @Override
  public Statement prepare(Connection connection, Integer transactionTimeout) 
throws SQLException {
    ErrorContext.instance().sql(boundSql.getSql());
    Statement statement = null;
    try {
      statement = instantiateStatement(connection);
      setStatementTimeout(statement, transactionTimeout);
      setFetchSize(statement);
      return statement;
    } catch (SQLException e) {
      closeStatement(statement);
      throw e;
    } catch (Exception e) {
      closeStatement(statement);
      throw new ExecutorException("Error preparing statement.  Cause: " + e, e);
    }
  }

  protected abstract Statement instantiateStatement(Connection connection) throws SQLException;

PreparedStatementHandler的instantiateStatement實(shí)現(xiàn),
connection.prepareStatement方法需要傳入sql語句,所以這里用到了boundSql

  @Override
  protected Statement instantiateStatement(Connection connection) throws SQLException {
    String sql = boundSql.getSql();
    if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
      String[] keyColumnNames = mappedStatement.getKeyColumns();
      if (keyColumnNames == null) {
        return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
      } else {
        return connection.prepareStatement(sql, keyColumnNames);
      }
    } else if (mappedStatement.getResultSetType() != null) {
      return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), 
ResultSet.CONCUR_READ_ONLY);
    } else {
      return connection.prepareStatement(sql);
    }
  }

5.填充sql參數(shù)

parameterize方法調(diào)用了ParameterHandler的setParameters方法

  @Override
  public void parameterize(Statement statement) throws SQLException {
    parameterHandler.setParameters((PreparedStatement) statement);
  }

結(jié)合了BoundSql的ParameterMapping和MappedStatement的parameterObject獲取需要設(shè)置的參數(shù),再結(jié)合TypeHandler再做數(shù)據(jù)轉(zhuǎn)換為PreparedStatement填充參數(shù)

  @Override
  public void setParameters(PreparedStatement ps) {
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings != null) {
      for (int i = 0; i < parameterMappings.size(); i++) {
        ParameterMapping parameterMapping = parameterMappings.get(i);
        if (parameterMapping.getMode() != ParameterMode.OUT) {
          Object value;
          String propertyName = parameterMapping.getProperty();
          if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
            value = boundSql.getAdditionalParameter(propertyName);
          } else if (parameterObject == null) {
            value = null;
          } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
            value = parameterObject;
          } else {
            MetaObject metaObject = configuration.newMetaObject(parameterObject);
            value = metaObject.getValue(propertyName);
          }
          TypeHandler typeHandler = parameterMapping.getTypeHandler();
          JdbcType jdbcType = parameterMapping.getJdbcType();
          if (value == null && jdbcType == null) {
            jdbcType = configuration.getJdbcTypeForNull();
          }
          try {
            typeHandler.setParameter(ps, i + 1, value, jdbcType);
          } catch (TypeException e) {
            throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
          } catch (SQLException e) {
            throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
          }
        }
      }
    }
  }

6. 執(zhí)行sql語句

調(diào)用PreparedStatement的execute方法

  @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    return resultSetHandler.<E> handleResultSets(ps);
  }

7.處理查詢結(jié)果集

如上代碼,結(jié)果集由ResultSetHandler處理

整體思路是抓住jdbc原生流程去對照分析

參考:
http://www.reibang.com/p/ec40a82cae28
https://blog.csdn.net/luanlouis/article/details/40422941

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末佳窑,一起剝皮案震驚了整個濱河市曹仗,隨后出現(xiàn)的幾起案子处面,更是在濱河造成了極大的恐慌骤肛,老刑警劉巖梨熙,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異哗戈,居然都是意外死亡荒椭,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進(jìn)店門笔链,熙熙樓的掌柜王于貴愁眉苦臉地迎上來段只,“玉大人,你說我怎么就攤上這事鉴扫≡拚恚” “怎么了坪创?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長莱预。 經(jīng)常有香客問我,道長依沮,這世上最難降的妖魔是什么涯贞? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮悉抵,結(jié)果婚禮上摘完,老公的妹妹穿的比我還像新娘。我一直安慰自己孝治,他們只是感情好列粪,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布审磁。 她就那樣靜靜地躺著,像睡著了一般岂座。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上费什,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天,我揣著相機(jī)與錄音瘩蚪,去河邊找鬼稿黍。 笑死疹瘦,一個胖子當(dāng)著我的面吹牛巡球,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播酣栈,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼鸯乃!你這毒婦竟也來了缨睡?” 一聲冷哼從身側(cè)響起陈辱,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤沛贪,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后水评,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體媚送,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年拿霉,在試婚紗的時候發(fā)現(xiàn)自己被綠了咱扣。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片绽淘。...
    茶點(diǎn)故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖闹伪,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情偏瓤,我是刑警寧澤硼补,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布已骇,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏鲤竹。R本人自食惡果不足惜辛藻,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一吱肌、第九天 我趴在偏房一處隱蔽的房頂上張望氮墨。 院中可真熱鬧,春花似錦桥氏、人聲如沸字支。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽苛萎。三九已至腌歉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間桂塞,已是汗流浹背阁危。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工狂打, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留混弥,地道東北人。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像粟瞬,于是被迫代替她去往敵國和親萤捆。 傳聞我的和親對象是個殘疾皇子俗或,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評論 2 354

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