本篇對mybatis從取到數據庫數據開始到映射成對象並返回的過程進行了詳細的分析。
轉換ResultSet成java對象
下面的代碼是PreparedStatementHandler中的
@Override
public <E> Cursor<E> queryCursor(Statement statement) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
//這裏是執行sql並得到ResultSet
ps.execute();
//這裏是真正處理ResultSet,將之轉換成java對象的地方
return resultSetHandler.<E> handleCursorResultSets(ps);
}
//resultSetHandler是DefaultResultSetHandler類型的
@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
final List<Object> multipleResults = new ArrayList<Object>();
int resultSetCount = 0;
//把statement中的ResultSet用ResultSetWrapper包裝起來(裏面有處理類對象的,列名,類名,列名映射等字段)
ResultSetWrapper rsw = getFirstResultSet(stmt);
//獲得mappedStatement中的ResultMaps(這個在初始化configuration的時候就已經生成好了,這裏只是取出來)
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
//如果rsw不爲空,但是resultMapCount小於1就報錯(很容易理解,就是有返回結果,但是沒有映射,所以mybatis不知道怎麼把結果轉換成java類,當然就報錯了)
validateResultMapsCount(rsw, resultMapCount);
while (rsw != null && resultMapCount > resultSetCount) {
//拿到resultMap
ResultMap resultMap = resultMaps.get(resultSetCount);
//處理ResultSet,將處理好的結果存到multipleResults(具體看下面的函數)
handleResultSet(rsw, resultMap, multipleResults, null);
//從上面這個函數出來的時候,multipleResults已經是存有結果的list了
//若stmt還有下一個ResultSet,則繼續循環
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
//若你的mapper配置文件中配了這個ResultSets屬性的話就取出來往下面走(不過一般都不會用到,最少LZ到現在還沒有用過)
String[] resultSets = mappedStatement.getResultSets();
if (resultSets != null) {
//不常用就不細細分析了,看完這整篇後,你對mybatis已經有了比較清晰的邏輯了
while (rsw != null && resultSetCount < resultSets.length) {
ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
if (parentMapping != null) {
String nestedResultMapId = parentMapping.getNestedResultMapId();
ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
handleResultSet(rsw, resultMap, null, parentMapping);
}
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
}
//最後返回結果集
return collapseSingleResultList(multipleResults);
}
//處理過程
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
try {
//如果有parentMapping則走下面
if (parentMapping != null) {
//進行處理
handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
} else {
//若resultHandler爲空則走下面
if (resultHandler == null) {
//初始化一個DefaultResultHandler
DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
//進行處理
handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
//multipleResults中加defaultResultHandler裏面的結果集
multipleResults.add(defaultResultHandler.getResultList());
} else {
//進行處理
handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
}
}
} finally {
// issue #228 (close resultsets)
//最後關閉ResultSet回到handleResultSets
closeResultSet(rsw.getResultSet());
}
}
//其實最後走的都是這個函數
public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
//看是否有嵌套的ResultMap,(比如使用了association, collection標籤)
if (resultMap.hasNestedResultMaps()) {
ensureNoRowBounds();
checkResultHandler();
//處理嵌套ResultMap
handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
} else {
//處理簡單的ResultMap(以這個爲例子)
handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
}
}
//處理簡單的ResultMap
private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
throws SQLException {
DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>();
//若有分頁,則先跳過前面的行數(因爲mybatis自帶的分頁是內存分頁,全部數據都取到服務器在進行分頁的)
skipRows(rsw.getResultSet(), rowBounds);
while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {//若要繼續處理列並且resultSet還有列則處理
//這步拿到要進行處理的resultmap
ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
//獲得整行的數據,根據map映射成java對象
Object rowValue = getRowValue(rsw, discriminatedResultMap);
//把結果存好,存在resultHandler中的list中(DefaultResultHandler中有一個字段 List<Object> list用來存儲這個結果)
storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
}
}
新建對象並賦值
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
final ResultLoaderMap lazyLoader = new ResultLoaderMap();
//創建一個對象來接受數據(根據配置生成相應的對象)
Object rowValue = createResultObject(rsw, resultMap, lazyLoader, null);
if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
final MetaObject metaObject = configuration.newMetaObject(rowValue);
boolean foundValues = this.useConstructorMappings;
//看是否需要自動映射
if (shouldApplyAutomaticMappings(resultMap, false)) {
//自動映射,把結果放到metaObject的originalObject也就是rowValue中(具體函數看下方)
foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
}
//通過屬性映射來找
foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
foundValues = lazyLoader.size() > 0 || foundValues;
//看這段貌似可以在configuration中設置若找不到就實例化一個初始的對象
rowValue = (foundValues || configuration.isReturnInstanceForEmptyRow()) ? rowValue : null;
}
//返回解析好的行值
return rowValue;
}
通過mapping映射屬性
private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
//這個是自動獲取mapping,也是比較重要的函數(下方有具體的分析)
List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
boolean foundValues = false;
if (autoMapping.size() > 0) {
for (UnMappedColumnAutoMapping mapping : autoMapping) {
//遍歷autoMapping
//從resultSet中解析出值
//解析的方法很簡單,就不跟了,大概就是調用typeHandler中的get函數,取出和列名相對應的值就行了
final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
if (value != null) {
//value不爲空說明找到了
foundValues = true;
}
if (value != null || (configuration.isCallSettersOnNulls() && !mapping.primitive)) {
// gcode issue #377, call setter on nulls (value is not 'found')
//找到了就把值設置到 metaObject中的objectWrapper中的object中。其實就是metaObject中的originalObject
metaObject.setValue(mapping.property, value);
}
}
}
//返回是否找到
return foundValues;
}
獲取Mapping
//創建自動映射的mapping
private List<UnMappedColumnAutoMapping> createAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
final String mapKey = resultMap.getId() + ":" + columnPrefix;
看緩存裏有沒有
List<UnMappedColumnAutoMapping> autoMapping = autoMappingsCache.get(mapKey);
//沒有就自己造
if (autoMapping == null) {
//存放映射的UnMappedColumnAutoMapping(一個對應一個字段映射)
autoMapping = new ArrayList<UnMappedColumnAutoMapping>();
//通過resultSetWrapper來創建未映射的字段的映射(具體方法看下面)
final List<String> unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);
for (String columnName : unmappedColumnNames) {
//輪詢unmappedColumnNames
String propertyName = columnName;
if (columnPrefix != null && !columnPrefix.isEmpty()) {
// When columnPrefix is specified,
// ignore columns without the prefix.
if (columnName.toUpperCase(Locale.ENGLISH).startsWith(columnPrefix)) {
propertyName = columnName.substring(columnPrefix.length());
} else {
continue;
}
}
//通過metaObject找到屬性
final String property = metaObject.findProperty(propertyName, configuration.isMapUnderscoreToCamelCase());
//如果屬性不爲空且有Set方法的話進入方法
if (property != null && metaObject.hasSetter(property)) {
//查看該字段set的是什麼類型
final Class<?> propertyType = metaObject.getSetterType(property);
//看typeHandlerRegistry有沒有處理這種類型的handle,有的話繼續
if (typeHandlerRegistry.hasTypeHandler(propertyType, rsw.getJdbcType(columnName))) {
//獲取TypeHandler
final TypeHandler<?> typeHandler = rsw.getTypeHandler(propertyType, columnName);
//生成UnMappedColumnAutoMapping放到autoMapping裏
autoMapping.add(new UnMappedColumnAutoMapping(columnName, property, typeHandler, propertyType.isPrimitive()));
} else {
configuration.getAutoMappingUnknownColumnBehavior()
.doAction(mappedStatement, columnName, property, propertyType);
}
} else{
configuration.getAutoMappingUnknownColumnBehavior()
.doAction(mappedStatement, columnName, (property != null) ? property : propertyName, null);
}
}
放到cache中
autoMappingsCache.put(mapKey, autoMapping);
}
//返回autoMapping(回到applyAutomaticMappings方法)
return autoMapping;
}
//ResultSetWrapper類中
public List<String> getUnmappedColumnNames(ResultMap resultMap, String columnPrefix) throws SQLException {
//看緩存有沒有,沒有自己造,有就直接返回
List<String> unMappedColumnNames = unMappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix));
if (unMappedColumnNames == null) {
//加載映射和沒映射字段
loadMappedAndUnmappedColumnNames(resultMap, columnPrefix);
unMappedColumnNames = unMappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix));
}
return unMappedColumnNames;
}
private void loadMappedAndUnmappedColumnNames(ResultMap resultMap, String columnPrefix) throws SQLException {
List<String> mappedColumnNames = new ArrayList<String>();
List<String> unmappedColumnNames = new ArrayList<String>();
final String upperColumnPrefix = columnPrefix == null ? null : columnPrefix.toUpperCase(Locale.ENGLISH);
//獲取已經映射過的column
final Set<String> mappedColumns = prependPrefixes(resultMap.getMappedColumns(), upperColumnPrefix);
//輪詢裏面ResultSetWrapper中的columnNames
for (String columnName : columnNames) {
//轉成大寫比對
final String upperColumnName = columnName.toUpperCase(Locale.ENGLISH);
//若mappedColumns中有此column
if (mappedColumns.contains(upperColumnName)) {
//加入到mappedColumnNames
mappedColumnNames.add(upperColumnName);
} else {
//否則放入unmappedColumnNames
unmappedColumnNames.add(columnName);
}
}
//最後將mappedColumnNames放到mappedColumnNamesMap
mappedColumnNamesMap.put(getMapKey(resultMap, columnPrefix), mappedColumnNames);
//最後將unmappedColumnNames放到unMappedColumnNamesMap
unMappedColumnNamesMap.put(getMapKey(resultMap, columnPrefix), unmappedColumnNames);
}
總結
其實就幾個重要的點。
1.創建對象
2.獲得resultHandle
3.獲得mapping
4.獲得typeHandle
5.通過handle和mapping給對象賦值,然後把對象存到resultHandle中
6.最後返回