MyBatis 源碼分析——映射結果


MyBatis最后一步一定是處理相關的結果——把數據映射成對應的模型對象。事實上在筆者看來如果讀者們了解了mybatis如何去執行數據庫,又是如何處理數據結果。那么就了解了mybatis的主要路線。因為不管是什么樣子的ORM最終都是要執行和處理結果的。而mybatis的亮點筆者也講了——管理sql語句。所以相對而言,處理結果就顯得十分的次要。但是筆者還是希望能在這里停留一下,研究他是如何處理結果。

mybatis處理結果的代碼都在DefaultResultSetHandler類里面。很抱歉的是筆者去掉了程序是如何執行到DefaultResultSetHandler類的。這里面還是希望讀者們自行的去跟蹤一下。DefaultResultSetHandler類的handleResultSets方法便是筆者切入的代碼。事實上當代碼執行到handleResultSets方法的時候,已經執行了數據庫。這在PreparedStatementHandler類里面就體現出來了。如下

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

用過mybatis的人都知道resultMap節點的作用。select節點的resultMap屬性就是用來指點使用哪一個resultMap節點。而返回的結果也是用這一個節點來設置的。如下

<select id="SelectProducts" resultMap="result" >
        select * from Products
        where #{0} > ProductID and ProductName like #{1}
</select>
<resultMap type="com.aomi.vo.Product" id="result" autoMapping="true">
</resultMap>

所以我可以設想一下。當我們拿到了數據的時候,希望數據變成我們指定的對象模型。那么我們就必須知道是什么類型。一般來講只要知道了什么類型就可以完成基本的映射條件。那么還會有可能出現一種希望自己去設計映射的字段和實例化對象。所以mybatis在這一方面也算是讓筆者很喜歡。細詳你可以查看一下官方的網站——XML映射文件

在映射的過程中,往往我們都需要知道表的字段。所以在DefaultResultSetHandler類的handleResultSets方法里面一開始就獲得了關於表的信息。表的信息都存放在ResultSetWrapper類里面。后面便就是根據這些信息來處理每一行的數據。我們還是看一下源碼吧。

 1 public List<Object> handleResultSets(Statement stmt) throws SQLException {
 2     ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
 3 
 4     final List<Object> multipleResults = new ArrayList<Object>();
 5 
 6     int resultSetCount = 0;
 7     ResultSetWrapper rsw = getFirstResultSet(stmt);
 8 
 9     List<ResultMap> resultMaps = mappedStatement.getResultMaps();
10     int resultMapCount = resultMaps.size();
11     validateResultMapsCount(rsw, resultMapCount);
12     while (rsw != null && resultMapCount > resultSetCount) {
13       ResultMap resultMap = resultMaps.get(resultSetCount);
14       handleResultSet(rsw, resultMap, multipleResults, null);
15       rsw = getNextResultSet(stmt);
16       cleanUpAfterHandlingResultSet();
17       resultSetCount++;
18     }
19 
20     String[] resultSets = mappedStatement.getResultSets();
21     if (resultSets != null) {
22       while (rsw != null && resultSetCount < resultSets.length) {
23         ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
24         if (parentMapping != null) {
25           String nestedResultMapId = parentMapping.getNestedResultMapId();
26           ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
27           handleResultSet(rsw, resultMap, null, parentMapping);
28         }
29         rsw = getNextResultSet(stmt);
30         cleanUpAfterHandlingResultSet();
31         resultSetCount++;
32       }
33     }
34 
35     return collapseSingleResultList(multipleResults);
36   }

筆者標出切入需要的倆個代碼段。一個就是獲得表信息的,另一個就是用於處理每一行數據的。事實上面的multipleResults變量就能說明一個問題——可能實現多個select句語的執行。所以相對的reusltMap也變成了集合類型。在XML上面就是用逗號隔開的。筆者沒有做過類似這種方式的代碼。筆者也是從源碼中看來的。他在MapperBuilderAssistant類的getStatementResultMaps方法里面。如下。

String[] resultMapNames = resultMap.split(",");

具體情況讀者們可以自己去實驗一下。

在筆者看來想要理解這一部分的關鍵點還在是getRowValue方法這邊。這里面就是我們所說的處理結果。根據reusltMap節點的信息判斷出是要自己根據類型生成對象。還是要通過reusltMap節點配置信息來生成對象。然后直行獲值和設值。如下。

 1 private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
 2     final ResultLoaderMap lazyLoader = new ResultLoaderMap();
 3     Object resultObject = createResultObject(rsw, resultMap, lazyLoader, null);
 4     if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
 5       final MetaObject metaObject = configuration.newMetaObject(resultObject);
 6       boolean foundValues = !resultMap.getConstructorResultMappings().isEmpty();
 7       if (shouldApplyAutomaticMappings(resultMap, false)) {
 8         foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
 9       }
10       foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
11       foundValues = lazyLoader.size() > 0 || foundValues;
12       resultObject = foundValues ? resultObject : null;
13       return resultObject;
14     }
15     return resultObject;
16   }

1.createResultObject方法:根據類型和配置信息生成對象。

2.applyAutomaticMappings方法:判斷是否是autoMapping="true",如果是就是自動映射賦值。

3.applyPropertyMappings方法:根據配置信息賦值。

接下來,就是各位讀者們根據自己的需要進行進一步的研究了。如果想知道是如何生成對象就是去看createResultObject方法。賦值的話就去看applyAutomaticMappings方法和applyPropertyMappings方法。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM