我們已經(jīng)知道了 Mapper 方法執(zhí)行的前因退敦,即:獲取語句+參數(shù)映射(MyBatis 源碼分析篇 6:Mapper 方法執(zhí)行的“前因”)≌晨В現(xiàn)在就讓我們來看看其“后果”:結(jié)果集映射。
參數(shù)映射是通過 TypeHandler 實(shí)現(xiàn)的侈百,那么同理瓮下,結(jié)果集映射也應(yīng)該是通過 TypeHandler 實(shí)現(xiàn)。
我們還是按照之前的方式钝域,使用 debug 在入口代碼上打斷點(diǎn)讽坏,步入源碼。入口代碼為:
List<Author> list = mapper.selectByName("Sylvia");
對(duì)應(yīng)的 SQL:
<resultMap id="AuthorMap" type="Author">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="sex" property="sex" />
<result column="phone" property="phone" />
</resultMap>
<select id="selectByName" resultMap="AuthorMap" >
select
id, name, sex, phone
from author
where name = #{name}
</select>
我們接著 MyBatis 源碼分析篇 5:Mapper 方法執(zhí)行之 Executor 一文最后我們找到的 JDBC 代碼位置 org.apache.ibatis.executor.statement.PreparedStatementHandler 的 query(...) 方法來看:
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.<E> handleResultSets(ps);
}
在執(zhí)行完 PreparedStatement 的 execute() 后例证,自然就要進(jìn)行結(jié)果的處理路呜,我們現(xiàn)在就進(jìn)入最后一行結(jié)果處理的代碼看看:
org.apache.ibatis.executor.resultset.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;
ResultSetWrapper rsw = getFirstResultSet(stmt);
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
validateResultMapsCount(rsw, resultMapCount);
while (rsw != null && resultMapCount > resultSetCount) {
ResultMap resultMap = resultMaps.get(resultSetCount);
handleResultSet(rsw, resultMap, multipleResults, null);
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
//忽略 resultSets 部分...
return collapseSingleResultList(multipleResults);
}
這個(gè)方法就是在獲取結(jié)果集并處理成我們最終想要的 List<Author> 類型的結(jié)果。我們提取一下關(guān)鍵步驟:
-
第一步:獲取 PreparedStatement 執(zhí)行數(shù)據(jù)庫操作后返回的 ResultSet织咧。
ResultSetWrapper rsw = getFirstResultSet(stmt);(方法的第 4 行代碼) -
第二步:獲取 ResultMap胀葱。
List<ResultMap> resultMaps = mappedStatement.getResultMaps();(方法的第 5 行代碼)
ResultMap resultMap = resultMaps.get(resultSetCount);(方法的第 9 行代碼) -
第三步:通過前兩步獲得的 ResultSet 和 ResultMap 處理獲得最終的 List<Author> 類型的結(jié)果。
handleResultSet(rsw, resultMap, multipleResults, null);(方法的第 10 行代碼)
現(xiàn)在笙蒙,我們就來對(duì)每一步進(jìn)行跟蹤分析抵屿。
1 獲取 ResultSet
ResultSetWrapper rsw = getFirstResultSet(stmt);
步入這行代碼,進(jìn)入本類的 getFirstResultSet(...) 方法:
private ResultSetWrapper getFirstResultSet(Statement stmt) throws SQLException {
ResultSet rs = stmt.getResultSet();
while (rs == null) {
// move forward to get the first resultset in case the driver
// doesn't return the resultset as the first result (HSQLDB 2.1)
if (stmt.getMoreResults()) {
rs = stmt.getResultSet();
} else {
if (stmt.getUpdateCount() == -1) {
// no more results. Must be no resultset
break;
}
}
}
return rs != null ? new ResultSetWrapper(rs, configuration) : null;
}
在上一篇關(guān)于獲取語句+參數(shù)映射的分析我們知道捅位,這個(gè)方法傳入的參數(shù) stmt 是通過動(dòng)態(tài)代理方法生成的晌该,那么上面這個(gè)方法中的第一行代碼:ResultSet rs = stmt.getResultSet(); 自然就會(huì)進(jìn)入動(dòng)態(tài)代理類 PreparedStatementLogger 中的 invoke(...) 方法來獲取 ResultSet:
@Override
public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, params);
}
if (EXECUTE_METHODS.contains(method.getName())) {
//這里省略了幾個(gè) else if
} else if ("getResultSet".equals(method.getName())) {
ResultSet rs = (ResultSet) method.invoke(statement, params);
return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack);
}
//這里還有其他代碼,繼續(xù)省略...
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
我們主要來看 if ("getResultSet".equals(method.getName() 這個(gè)判斷后的代碼:如果現(xiàn)在執(zhí)行的方法是 getResultSet(...)绿渣,那么首先會(huì)獲取一個(gè) ResultSet 的實(shí)例,接著會(huì)通過 ResultSetLogger.newInstance() 獲取的 ResultSet 覆蓋掉這個(gè) ResultSet燕耿。同獲取 PreparedStatement 實(shí)例非常相似中符,這里采用同樣的方式:動(dòng)態(tài)代理來通過 ResultSetLogger 代理類獲取 ResultSet 實(shí)例。此處便不再贅述誉帅。
分析到這里我們就拿到了新出爐的 ResultSet淀散,并將它封裝到了 ResultSetWrapper 類中以供接下來的使用。
2 獲取 ResultMap
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
ResultMap resultMap = resultMaps.get(resultSetCount);
關(guān)于獲取 ResultMap蚜锨,它是直接從 MappedStatement 中拿到的档插,而這個(gè) MappedStatement 實(shí)例的內(nèi)容是在獲取 SqlSessionFactory 時(shí)填充的,關(guān)于這一點(diǎn)我們?cè)?MyBatis 源碼分析篇 6:Mapper 方法執(zhí)行的“前因” 中已經(jīng)詳細(xì)分析過亚再,此處便不再贅述郭膛,感興趣的同學(xué)可以查看之前的篇章并結(jié)合自己實(shí)踐跟蹤源碼來回憶一下。
這個(gè)時(shí)候獲得的 ResultMap 是這個(gè)樣子的:
可以看到 ResultMap 已經(jīng)包含了每個(gè)屬性的結(jié)果映射 Map氛悬、要映射的列和屬性则剃,用于接下來的結(jié)果映射耘柱。
3 獲取 List<Object>
handleResultSet(rsw, resultMap, multipleResults, null);
通過前兩步,我們已經(jīng)拿到了 ResultSet 和 ResultMap 了棍现,現(xiàn)在我們就要來通過它們處理得到最終我們想要的 List<Object> 了调煎。
首先要說明的一點(diǎn)是,這行代碼是在 while 循環(huán)中己肮,即通過遍歷我們拿到的 ResultSetWrapper (封裝了 ResultSet)士袄,來循環(huán)處理每一條結(jié)果集。現(xiàn)在就讓我們步入這行代碼谎僻,進(jìn)入本類的 handleResultSet(...) 方法:
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
try {
if (parentMapping != null) {
handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
} else {
if (resultHandler == null) {
DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
multipleResults.add(defaultResultHandler.getResultList());
} else {
handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
}
}
} finally {
// issue #228 (close resultsets)
closeResultSet(rsw.getResultSet());
}
}
對(duì)于上面這個(gè)方法娄柳,其中第 6 、7 行代碼為我們要跟蹤的核心功能戈稿。其中西土,第 6 行代碼通過 ObjectFactory 生成 DefaultResultHandler,該 DefaultResultHandler 持有生成的 List 實(shí)例鞍盗。第 7 行代碼則是給第 6 行代碼執(zhí)行生成的 List 填充值需了。而第 8 行代碼則是將分頁處理過后的 List<Object> 加入 List 類型的 multipleResults 中返回。
現(xiàn)在我們就來步入第 6 行核心代碼看看:
public DefaultResultHandler(ObjectFactory objectFactory) {
list = objectFactory.create(List.class);
}
在 DefaultResultHandler 類的構(gòu)造方法中般甲,調(diào)用了 ObjectFactory 的 create(...) 方法肋乍。想必大家對(duì) ObjectFactory 已經(jīng)不再陌生,在前面文檔篇關(guān)于 ObjectFactory 的學(xué)習(xí)中我們知道:MyBatis 在獲取到結(jié)果集后會(huì)通過 ObjectFactory 來創(chuàng)建結(jié)果對(duì)象的實(shí)例敷存。它也是在 MyBatis 讀取 Configuration XML 的時(shí)候存入的墓造。也就是說,如果我們沒有在 mybatis-config.xml 中配置自定義的 objectFactory锚烦,它會(huì)使用默認(rèn)的 DefaultObjectFactory觅闽,默認(rèn)只是實(shí)例化一個(gè)目標(biāo)類。
為了印證這一點(diǎn)涮俄,我們跟蹤 create(...) 方法來看一下 DefaultObjectFactory 中的實(shí)現(xiàn):
@Override
public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
Class<?> classToCreate = resolveInterface(type);
// we know types are assignable
return (T) instantiateClass(classToCreate, constructorArgTypes, constructorArgs);
}
上面這個(gè)方法第 1 行代碼得到了 ArrayList 類型蛉拙,繼續(xù)步入:
private <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
try {
Constructor<T> constructor;
if (constructorArgTypes == null || constructorArgs == null) {
constructor = type.getDeclaredConstructor();
if (!constructor.isAccessible()) {
constructor.setAccessible(true);
}
return constructor.newInstance();
}
constructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[constructorArgTypes.size()]));
if (!constructor.isAccessible()) {
constructor.setAccessible(true);
}
return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));
} catch (Exception e) {
StringBuilder argTypes = new StringBuilder();
if (constructorArgTypes != null && !constructorArgTypes.isEmpty()) {
for (Class<?> argType : constructorArgTypes) {
argTypes.append(argType.getSimpleName());
argTypes.append(",");
}
argTypes.deleteCharAt(argTypes.length() - 1); // remove trailing ,
}
StringBuilder argValues = new StringBuilder();
if (constructorArgs != null && !constructorArgs.isEmpty()) {
for (Object argValue : constructorArgs) {
argValues.append(String.valueOf(argValue));
argValues.append(",");
}
argValues.deleteCharAt(argValues.length() - 1); // remove trailing ,
}
throw new ReflectionException("Error instantiating " + type + " with invalid types (" + argTypes + ") or values (" + argValues + "). Cause: " + e, e);
}
}
顯而易見,原來 MyBatis 使用 ObjectFactory 生成目標(biāo)類的實(shí)例彻亲,是通過反射實(shí)現(xiàn)的孕锄!對(duì)于反射,我想大家已經(jīng)很熟悉了苞尝,這里就不展開分析了畸肆。
現(xiàn)在我們拿到了 ArrayList 實(shí)例,接下來就剩下往里面塞值了宙址。我們接著看前面提到的第 7 行代碼:handleResultSet(...) 方法的 handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);轴脐。
進(jìn)入 org.apache.ibatis.executor.resultset.DefaultResultSetHandler 類的 handleRowValuesForSimpleResultMap(...) 方法:
private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
throws SQLException {
DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>();
skipRows(rsw.getResultSet(), rowBounds);
while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {
ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
Object rowValue = getRowValue(rsw, discriminatedResultMap);
storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
}
}
其中,方法的第 2 行代碼是要進(jìn)行邏輯分頁的,這一點(diǎn)我們已經(jīng)在 MyBatis 最佳實(shí)踐篇 1:分頁 一文中分析過豁辉,這里便不再贅述令野。我們重點(diǎn)來看 while 循環(huán)中的代碼,該 while 循環(huán)遍歷 ResultSet徽级。
循環(huán)中的第 1 行代碼的作用是對(duì) ResultMap 進(jìn)行進(jìn)一步的處理:判斷你是否定義了 Discriminator气破,如果定義了,它會(huì)去取你在 Discriminator 中指定的 ResultMap餐抢。
循環(huán)中的第 2 行代碼的作用是獲取 Bean 對(duì)象的實(shí)例并為該 Bean 對(duì)象賦值现使。
循環(huán)中的第 3 行代碼的作用很簡(jiǎn)單,就是將獲取到的 Bean 對(duì)象加入到 List 集合中旷痕。
我們想要知道 MyBatis 是如何將數(shù)據(jù)庫獲取到的每一條記錄變成我們想要的集合中的 Java 對(duì)象的碳锈,所以第 2 行代碼是我們要關(guān)注的重點(diǎn)。
步入這行代碼欺抗,進(jìn)入 getRowValue() 方法:
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)) {
foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
}
foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
foundValues = lazyLoader.size() > 0 || foundValues;
rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
}
return rowValue;
}
我們來分析一下這個(gè)方法的主要功能:
- 第一步:獲取 Bean 對(duì)象
Object rowValue = createResultObject(rsw, resultMap, lazyLoader, null);(方法中第 2 行) - 第二步:為 Bean 對(duì)象賦值售碳,填充字段
foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;(方法中第 9 行)
現(xiàn)在我們就來依次看一下具體是如何實(shí)現(xiàn)每一步的。
首先我們跟入第一步的代碼绞呈,直到進(jìn)入 org.apache.ibatis.executor.resultset.DefaultResultSetHandler 類的 createResultObject() 方法:
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix)
throws SQLException {
final Class<?> resultType = resultMap.getType();
final MetaClass metaType = MetaClass.forClass(resultType, reflectorFactory);
final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();
if (hasTypeHandlerForResultObject(rsw, resultType)) {
return createPrimitiveResultObject(rsw, resultMap, columnPrefix);
} else if (!constructorMappings.isEmpty()) {
return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix);
} else if (resultType.isInterface() || metaType.hasDefaultConstructor()) {
return objectFactory.create(resultType);//對(duì)贸人!就是這一行!
} else if (shouldApplyAutomaticMappings(resultMap, false)) {
return createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs, columnPrefix);
}
throw new ExecutorException("Do not know how to create an instance of " + resultType);
}
這個(gè)方法首先要通過 ResultMap 獲取要?jiǎng)?chuàng)建對(duì)象的類型佃声,然后根據(jù)該類型判斷要如何實(shí)例化艺智。因?yàn)樵谠摐y(cè)試中,我們要獲取一個(gè)普通 Bean 對(duì)象 Author 的實(shí)例圾亏,它有默認(rèn)的構(gòu)造方法十拣,所以會(huì)進(jìn)入方法的第 9 行代碼:return objectFactory.create(resultType); 這行代碼我想大家已經(jīng)不陌生了,在獲取 ArrayList 對(duì)象的時(shí)候也是通過 ObjectFactory 的 create() 實(shí)現(xiàn)的志鹃。那么也就是說夭问,對(duì)于結(jié)果集 List 的 Bean 對(duì)象的實(shí)例化是通過 ObjectFactory 實(shí)現(xiàn)的,也就是通過反射實(shí)現(xiàn)的曹铃!
現(xiàn)在我們拿到了 Bean 對(duì)象甲喝,剩下的就是為這個(gè) Bean 對(duì)象的每個(gè)字段根據(jù)我們查詢的結(jié)果賦值了。我們進(jìn)入 applyPropertyMappings 方法看一下:
private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix)
throws SQLException {
final List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
boolean foundValues = false;
final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
for (ResultMapping propertyMapping : propertyMappings) {
String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
//略...
if (propertyMapping.isCompositeResult()
|| (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)))
|| propertyMapping.getResultSet() != null) {
Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);
//略...
if (value != null) {
foundValues = true;
}
if (value != null || (configuration.isCallSettersOnNulls() && !metaObject.getSetterType(property).isPrimitive())) {
// gcode issue #377, call setter on nulls (value is not 'found')
metaObject.setValue(property, value);
}
}
}
return foundValues;
}
上面這個(gè)方法首先通過 ResultMap 獲取到要映射的列名列表 mappedColumnNames 和屬性結(jié)果映射列表
propertyMappings铛只。然后遍歷 propertyMappings,從 ResultSet 中獲取每個(gè)列對(duì)應(yīng)的值并將屬性值存儲(chǔ)到我們之前獲得的 Author 對(duì)象中糠溜。其中淳玩,方法的第 10 行:Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix); 為獲取值,我們跟蹤一下:
private Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix)
throws SQLException {
if (propertyMapping.getNestedQueryId() != null) {
//略...
} else {
final TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();
final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
return typeHandler.getResult(rs, column);
}
}
該方法首先獲取該映射的 typeHandler 和列名非竿,用 typeHandler 來獲韧勺拧:
@Override
public Integer getNullableResult(ResultSet rs, String columnName)
throws SQLException {
return rs.getInt(columnName);
}
這是我們熟悉的 JDBC 代碼,但是需要提醒一下,我們前面分析過承匣,這里的 ResultSet 是通過動(dòng)態(tài)代理生成的蓖乘,所以調(diào)用其方法不是直接從我們熟知的 ResultSet 獲取,而是通過代理類 ResultSetLogger 來實(shí)現(xiàn)韧骗,那么接著它會(huì)進(jìn)入到 ResultSetLogger 的 invoke():
@Override
public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, params);
}
Object o = method.invoke(rs, params);
if ("next".equals(method.getName())) {
if (((Boolean) o)) {
rows++;
if (isTraceEnabled()) {
ResultSetMetaData rsmd = rs.getMetaData();
final int columnCount = rsmd.getColumnCount();
if (first) {
first = false;
printColumnHeaders(rsmd, columnCount);
}
printColumnValues(columnCount);
}
} else {
debug(" Total: " + rows, false);
}
}
clearColumnInfo();
return o;
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
在該方法的第 5 行代碼我們就得到了最終的值嘉抒。好,現(xiàn)在我們拿到了字段的值袍暴,就差把值塞進(jìn) Author 里了些侍。我們回到 DefaultResultSetHandler 的 applyPropertyMappings(...) 方法中,跟入 metaObject.setValue(property, value); 這行代碼政模,直到進(jìn)入 BeanWrapper 類的 setBeanProperty(...):
private void setBeanProperty(PropertyTokenizer prop, Object object, Object value) {
try {
Invoker method = metaClass.getSetInvoker(prop.getName());
Object[] params = {value};
try {
method.invoke(object, params);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
} catch (Throwable t) {
throw new ReflectionException("Could not set property '" + prop.getName() + "' of '" + object.getClass() + "' with value '" + value + "' Cause: " + t.toString(), t);
}
}
很明顯岗宣,這里也用的是反射,try 中的第一行就是在獲取要調(diào)用的方法淋样。MyBatis 中用到了大量的反射耗式,源碼中專門有一個(gè) reflection 模塊,就是封裝 Java 反射的趁猴。我們先跟進(jìn)去看一下:
public Invoker getSetInvoker(String propertyName) {
Invoker method = setMethods.get(propertyName);
if (method == null) {
throw new ReflectionException("There is no setter for property named '" + propertyName + "' in '" + type + "'");
}
return method;
}
我們看方法的第 1 行代碼:Invoker method = setMethods.get(propertyName);刊咳,傳入的參數(shù)是屬性名,setMethods 是個(gè) HashMap<String, Invoker>躲叼,key 即為屬性名芦缰。setMethods 預(yù)先存儲(chǔ)了 Bean 中所有屬性及其對(duì)應(yīng)的包含 set 方法的 MethodInvoker。
那么現(xiàn)在我們就可以判斷出來枫慷,第 1 行代碼通過屬性名獲取到對(duì)應(yīng)的包含了該屬性的 set 方法的 MethodInvoker让蕾。然后返回到 BeanWrapper 的 setBeanProperty(...) 方法中,我們繼續(xù)跟入 try 中的代碼:method.invoke(object, params);:
@Override
public Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException {
return method.invoke(target, args);
}
其中或听,這時(shí)的 method 為 public void com.zhaoxueer.learn.entity.Author.setName(java.lang.String)探孝,target 為 Author 對(duì)象,args 即為屬性對(duì)應(yīng)的值誉裆。那么接下來它就會(huì)進(jìn)入我們定義的 Bean顿颅,即 Author 中的 setName(...) 方法:
public void setName(String name) {
this.name = name;
}
這樣,循環(huán)完所有的屬性結(jié)果映射后足丢,我們就會(huì)獲得一個(gè)完美的已經(jīng)被賦了數(shù)據(jù)庫查詢結(jié)果的值的 Author 對(duì)象粱腻。當(dāng)我們執(zhí)行完所有的循環(huán)后,我們就會(huì)最終拿到我們想要的 List<Author> 的結(jié)果列表斩跌。至此绍些,大功告成!
最后耀鸦,簡(jiǎn)單總結(jié)一下 Mapper 方法執(zhí)行柬批。我們分了四篇分別來分析了 Mapper 方法是如何執(zhí)行的啸澡,以及 Mapper 方法執(zhí)行的前因(獲取語句+參數(shù)映射)后果(結(jié)果映射)。其中氮帐,從獲取 Connection嗅虏、獲取 PreparedStatement 到獲取 ResultSet 都使用動(dòng)態(tài)代理的方式來實(shí)現(xiàn),分步打印日志的同時(shí)獲取對(duì)象上沐。對(duì)于參數(shù)和結(jié)果映射都使用了 TypeHandler皮服,我們不需要為每個(gè)要映射的屬性或字段顯式配置 TypeHandler,MyBatis 會(huì)根據(jù)參數(shù)值和結(jié)果字段值實(shí)際的類型來自動(dòng)推算 TypeHandler奄容。對(duì)于結(jié)果映射冰更,當(dāng)我們從數(shù)據(jù)庫獲得 ResultSet 之后,通過使用 ObjectFactory 來反射來實(shí)例化對(duì)應(yīng)的 Java 對(duì)象昂勒,并通過反射的方式將字段值存儲(chǔ)到 Java 對(duì)象中蜀细。
附:
當(dāng)前版本:mybatis-3.5.0
官網(wǎng)文檔:MyBatis
項(xiàng)目實(shí)踐:MyBatis Learn
手寫源碼:MyBatis 簡(jiǎn)易實(shí)現(xiàn)