mybatis查詢語句的背後之封裝數據

 轉載請註明出處。。。html

1、前言

繼上一篇mybatis查詢語句的背後,這一篇主要圍繞着mybatis查詢的後期操做,即跟數據庫交互的時候。因爲本人也是一邊學習源碼一邊記錄,內容不免有錯誤或不足之處,還望諸位指正,本文只可當參考做用。謹記! java

2、分析

繼上一篇博文的查詢例子,mybatis在最後的查詢最終會走SimpleExecutor類的doQuery方法,sql

 1  @Override
 2   public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
 3     Statement stmt = null;
 4     try {
 5       Configuration configuration = ms.getConfiguration();
 6       // 這裏也就是採用了策略模式(我的感受有點像),實際的statementHandler爲routingStatementHandler
 7       StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
 8       stmt = prepareStatement(handler, ms.getStatementLog());
 9       // 雖然是執行的routingStatementHandler.query,但返回結果的仍是PreparedStatementHandler處理
10       return handler.query(stmt, resultHandler);
11     } finally {
12       closeStatement(stmt);
13     }
14   }
15 
16 private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
17     Statement stmt;
18    // 使用了代理模式,也能夠理解爲對connection進行了一層包裝,這裏的做用就是加了log處理
19     Connection connection = getConnection(statementLog);
20    //進行預編譯,即相似jdbc的 sql,如 select * from user where id=?
21     stmt = handler.prepare(connection, transaction.getTimeout());
22     // 對執行查詢的sql進行參數設置
23     handler.parameterize(stmt);
24     return stmt;
25   }

關於 handler.prepare的做用這裏簡單介紹下,不作代碼分析。數據庫

會設置fetchSize,做用就是一次性從數據庫抓取數據,好像默認值是10條,若是每次只抓取一條,則進行rs.next的時候,會再次查庫。mybatis

若是是insert操做,而且數據庫主鍵自增且還設置了能夠返回主鍵,則會還作獲取主鍵的操做。app

先從設置參數提及,也就是handler.parameterize。先看下源碼,具體位置在DefaultParameterHandler類裏面ide

 1 @Override
 2   public void setParameters(PreparedStatement ps) {
 3     ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
 4     // 獲取配置文件裏面的sql參數信息,如sql爲select * from user where id=#{userId,jdbcType=INTEGER}
 5     // ParameterMapping 記錄了參數名也就是userId,還有記錄了對應的jdbc類型,還有對應的javaType等等,具體能夠debug看下
 6     List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
 7     if (parameterMappings != null) {
 8       for (int i = 0; i < parameterMappings.size(); i++) {
 9         ParameterMapping parameterMapping = parameterMappings.get(i);
10         if (parameterMapping.getMode() != ParameterMode.OUT) {
11           Object value;
12           String propertyName = parameterMapping.getProperty();
13           // 若是爲true,那麼sql參數中有相似 user.name 格式
14           if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
15             value = boundSql.getAdditionalParameter(propertyName);
16           } else if (parameterObject == null) {
17             value = null;
18           } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
19             value = parameterObject;
20           } else {
21             // metaObject 相似一個工具類,它裏面有一個反射工廠,能夠專門解析一個類的信息,如字段的setter/getter/屬性信息,這裏不作多餘介紹
22             // 一、下面詳細介紹 
23             MetaObject metaObject = configuration.newMetaObject(parameterObject);
24             value = metaObject.getValue(propertyName);// 取值
25           }
26           // 獲取對應的typeHandler,通常狀況不設置的話,基本都是ObjectTypeHandler
27           TypeHandler typeHandler = parameterMapping.getTypeHandler();
28           JdbcType jdbcType = parameterMapping.getJdbcType();
29           if (value == null && jdbcType == null) {
30             jdbcType = configuration.getJdbcTypeForNull();
31           }
32           try {
33             // 進行設值
34             typeHandler.setParameter(ps, i + 1, value, jdbcType);
35           } catch (TypeException e) {
36             throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
37           } catch (SQLException e) {
38             throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
39           }
40         }
41       }
42     }
43   }

 對於上述代碼中的一部分這裏負責將parameterObject的裏面的值整出來(也就是傳入的參數),若是參數是map結構,就從map裏面取值,若是不是,如單個非javabean參數,則直接取值,若是是單個javabean,則經過metaObject類轉換成一個BeanWrapper,進行取值工具

這段代碼也就負責對預編譯後的sql設置參數,這裏邏輯主要是圍繞如下步驟進行得,學習

    獲取參數名,獲取參數值,獲取參數類型,而後作進行設值操做fetch

 

 1  /**
 2  * mybatis數據處理有單結果集和多結果集處理,通常多結果集出現存儲過程當中,若是存儲過程當中寫了兩條select語句,如
 3  * select * from user , select * from classes   這種狀況這裏不作介紹,由於本人用的很少,理解的也不是很透徹。
 4  * 這裏很少作介紹,這裏只針對簡單映射作一個大概介紹
 5  *
 6  */
 7 public List<Object> handleResultSets(Statement stmt) throws SQLException {
 8     ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
 9     // 保存查詢結果
10     final List<Object> multipleResults = new ArrayList<>();
11 
12     int resultSetCount = 0;
13     // 獲取第一條數據
14     ResultSetWrapper rsw = getFirstResultSet(stmt);
15     // 若是不是多結果集映射,通常resultMaps的大小爲1
16     // resultMap中存儲的有類的字段屬性,數據庫字段名稱等信息
17     List<ResultMap> resultMaps = mappedStatement.getResultMaps();
18     int resultMapCount = resultMaps.size();
19     // 校驗數據的正確性
20     validateResultMapsCount(rsw, resultMapCount);
21     while (rsw != null && resultMapCount > resultSetCount) {
22       ResultMap resultMap = resultMaps.get(resultSetCount);
23       // 處理結果集映射
24       handleResultSet(rsw, resultMap, multipleResults, null);
25       rsw = getNextResultSet(stmt);
26       cleanUpAfterHandlingResultSet();
27       resultSetCount++;
28     }
29     // 處理slect 標籤的resultSets屬性,多個用逗號隔開,我的幾乎沒用過,略過
30     String[] resultSets = mappedStatement.getResultSets();
31     if (resultSets != null) {
32       while (rsw != null && resultSetCount < resultSets.length) {
33         ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
34         if (parentMapping != null) {
35           String nestedResultMapId = parentMapping.getNestedResultMapId();
36           ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
37           handleResultSet(rsw, resultMap, null, parentMapping);
38         }
39         rsw = getNextResultSet(stmt);
40         cleanUpAfterHandlingResultSet();
41         resultSetCount++;
42       }
43     }
44 
45     return collapseSingleResultList(multipleResults);
46   }

 

以上代碼就是爲結果映射作一個鋪墊,重點是在hanleResultSet方法裏,

 1 private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
 2     try {// 針對簡單映射,parentMapping是爲Null的
 3       if (parentMapping != null) {
 4         handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
 5       } else {
 6         // 默認使用defaultResultHandler,如需使用自定義的,則可在傳參加入resultHandler接口實現類
 7         if (resultHandler == null) {
 8           DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
 9           // 處理結果,結果存在resultHandler裏
10           handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
11           multipleResults.add(defaultResultHandler.getResultList());
12         } else {
13           handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
14         }
15       }
16     } finally {
17       // issue #228 (close resultsets)
18       closeResultSet(rsw.getResultSet());
19     }
20   }
public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
   // 處理有嵌套映射的狀況
    if (resultMap.hasNestedResultMaps()) {
      ensureNoRowBounds();
      checkResultHandler();
      handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    } else {//沒有嵌套映射
      handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    }
  }

 

 1 private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
 2       throws SQLException {
 3     DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
 4     ResultSet resultSet = rsw.getResultSet();
 5     // 跳過多少行,到達指定記錄位置,如在傳參的時候傳入了rowBounds,則會根據該類的offset值跳到指定記錄位置
 6     skipRows(resultSet, rowBounds);
 7     // shouldProcessMoreRows 用來檢測是否能繼續對後續的結果進行映射
 8     while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
 9       //用來處理resultMap節點中配置了discriminator節點,這裏忽略掉
10       ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
11       // 獲得的結果就是sql執行後的一行記錄,如返回User對象信息,則rowValue就表明一個user實例,裏面已經有值了
12       Object rowValue = getRowValue(rsw, discriminatedResultMap, null);
13       //保存數據
14       storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
15     }
16   }
 1 private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
 2     final ResultLoaderMap lazyLoader = new ResultLoaderMap();
 3     // 建立對象,能夠理解爲對resultMap節點的type屬性值,進行了反射處理,獲得了一個對象,但屬性值都是默認值。
 4     Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
 5     if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
 6       final MetaObject metaObject = configuration.newMetaObject(rowValue);
 7       boolean foundValues = this.useConstructorMappings;
 8       //是否須要自動映射,有三種映射,分別爲None,partial,full,默認第二種,處理非嵌套映射,可經過autoMappingBehavior 配置
 9       if (shouldApplyAutomaticMappings(resultMap, false)) {
10         // 映射resultMap中未明確指定的列,如類中含有username屬性,可是resultMap中沒配置,則經過這個進行數據映射,仍是能夠查詢到結果
11         foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
12       }
13       // 處理resultMap中指定的列
14       foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
15       foundValues = lazyLoader.size() > 0 || foundValues;
16       // 若是沒查詢到結果,但配置可返回空對象(指的是沒有設置屬性值得對象),則返回空對象,不然返回null
17       rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
18     }
19     return rowValue;
20   }

 

這裏只介紹resultMap中有明確指定的列

 1 private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix)
 2       throws SQLException {
 3      // 獲取數據字段名
 4     final List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
 5     boolean foundValues = false;
 6     // 獲取的數據就是resultMap節點中配置的result節點,有多個result節點,這個集合大小就是多少
 7     // 裏面存儲的是屬性名/字段名等信息
 8     final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
 9     for (ResultMapping propertyMapping : propertyMappings) {
10       String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
11       // 是否有嵌套映射
12       if (propertyMapping.getNestedResultMapId() != null) {
13         // the user added a column attribute to a nested result map, ignore it
14         column = null;
15       }
16       // 針對1來講通常常與嵌套查詢配合使用
17       // 2 判斷屬性基本映射
18       // 3 多結果集的一個處理
19       if (propertyMapping.isCompositeResult()// 1
20           || (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)))// 2
21           || propertyMapping.getResultSet() != null) {// 3
22         // 獲取當前column字段對於的值,有用到typeHandler來進行參數的一個轉換
23         Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);
24         
25         //獲取類的屬性字段名
26         final String property = propertyMapping.getProperty();
27         if (property == null) {
28           continue;
29         } else if (value == DEFERRED) {// 相似佔位符。處理懶加載數據
30           foundValues = true;
31           continue;
32         }
33         if (value != null) {
34           foundValues = true;
35         }
36         if (value != null || (configuration.isCallSettersOnNulls() && !metaObject.getSetterType(property).isPrimitive())) {
37           // 進行設置屬性值
38           metaObject.setValue(property, value);
39         }
40       }
41     }
42     return foundValues;
43   }

 

或許有人奇怪爲啥沒看到查詢的對象有set操做,值就到了對象裏面去了,這裏全是metaObject給你操做了,具體的,你們能夠自行了解這個類,只能說這個類的功能很強大。

以上就是本文所有內容,

--------------------------------------------------------------------------------------------------------------------------分界線----------------------------------------------------------------------------------------------

相關文章
相關標籤/搜索