轉載請註明出處。。。html
繼上一篇mybatis查詢語句的背後,這一篇主要圍繞着mybatis查詢的後期操做,即跟數據庫交互的時候。因爲本人也是一邊學習源碼一邊記錄,內容不免有錯誤或不足之處,還望諸位指正,本文只可當參考做用。謹記! java
繼上一篇博文的查詢例子,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給你操做了,具體的,你們能夠自行了解這個類,只能說這個類的功能很強大。
以上就是本文所有內容,
--------------------------------------------------------------------------------------------------------------------------分界線----------------------------------------------------------------------------------------------