MyBatis 源码学习13——ResultMap
一、ResultMap:
ResultMap:
保存Java實體屬性與數據庫表字段之間的映射關系,是實現級聯映射和懶加載機制的基礎。
MyBatis是一個半自動化的ORM框架,可以將數據庫中的記錄轉換為Java實體對象,但是Java實體屬性通常采用駝峰命名法,而數據庫字段習慣采用下畫線分割命名法,因此需要用戶指定Java實體屬性與數據庫表字段之間的映射關系。
MyBatis的Mapper配置中提供了一個<resultMap>標簽,用于建立數據庫字段與Java實體屬性之間的映射關系。
<resultMap>子標簽:
? <constructor>:該標簽用于建立構造器映射。該標簽有兩個子標簽,<idArg>標簽用于配置主鍵映射,標記出主鍵,可以提高整體性能;<arg>標簽用于配置普通字段的映射。
? <id>:用于配置數據庫主鍵映射,標記出數據庫主鍵,有助于提高整體性能。
? <result>:用于配置數據庫字段與Java實體屬性之間的映射關系。
? <association>:用于配置一對一關聯映射,可以關聯一個外部的查詢Mapper或者配置一個嵌套的ResultMap。
? <collection>:用于配置一對多關聯映射,可以關聯一個外部的查詢Mapper或者配置一個嵌套的ResultMap。
? <discriminator>:用于配置根據字段值使用不同的ResultMap。該標簽有一個子標簽,<case>標簽用于枚舉字段值對應的ResultMap,類似于Java中的switch語法。
ResultMap解析過程:
MyBatis在啟動時,所有配置信息都會被轉換為Java對象,標簽信息會轉為ResultMap對象。
ResultMap類:
? Id:通過<resultMap>標簽的id屬性和Mapper命名空間組成的全局唯一的Id。
? Type:通過<resultMap>標簽的type屬性指定與數據庫表建立映射的Java實體。
? resultMappings:通過<result>標簽配置的所有數據庫字段與Java實體屬性之間的映射信息。
? idResultMappings:通過<id>標簽配置的數據庫主鍵與Java實體屬性的映射信息。需要注意的是,<id>標簽與<result>標簽沒有本質的區別。
? constructorResultMappings:通過<constructor>標簽配置的構造器映射信息。
? propertyResultMappings:通過<result>標簽配置的數據庫字段與Java實體屬性的映射信息。
? mappedColumns:該屬性存放所有映射的數據庫字段。當使用columnPrefix屬性配置了前綴時,MyBatis會對mappedColumns屬性進行遍歷,為所有數據庫字段追加columnPrefix屬性配置的前綴。
? mappedProperties:該屬性存放所有映射的Java實體屬性信息。
? discriminator:該屬性為在<resultMap>標簽中通過<discriminator>標簽配置的鑒別器信息。
? hasNestedResultMaps:該屬性用于標識是否有嵌套的ResultMap,當使用<association>或<collection>標簽以JOIN查詢方式配置一對一或一對多級聯映射時,<association>或<collection>標簽相當于一個嵌套的ResultMap,因此hasNestedResultMaps屬性值為true。
? hasNestedQueries:該屬性用于標識是否有嵌套的查詢,當使用<association>或<collection>標簽關聯一個外部的查詢Mapper建立一對一或一對多級聯映射時,hasNestedQueries屬性值為true。
? autoMapping:autoMapping屬性為true,表示開啟自動映射,即使未使用<result>或<id>標簽配置映射字段,MyBatis也會自動對這些字段進行映射。
<resultMap>標簽解析生成ResultMap對象的過程:
MyBatis中的Mapper配置信息解析是通過XMLMapperBuilder類完成的,該類提供了一個parse()方法,用于解析Mapper中的所有配置信息:
在XMLMapperBuilder的parse()方法中,調用XMLMapperBuilder類的configurationElement()方法,然后configurationElement()方法又調用resultMapElements()方法對所有<resultMap>標簽進行解析。
resultMapElements()方法最終會調用重載的resultMapElement()方法對每個<resultMap>標簽進行解析:
1.首先獲取<resultMap>標簽的所有屬性信息,
2.然后對<id>等子標簽進行解析,通過buildResultMappingFromContext(),創建字段的ResultMapping對象
3.接著創建一個ResultMapResolver對象,
4.調用ResultMapResolver對象的resolve()方法返回一個ResultMap對象。
ResultMapResolver對象的resolve()方法調用了MapperBuilderAssistant的addResultMap()方法:
1.首先判斷該ResultMap是否繼承了其他ResultMap。如果是,則獲取父ResultMap對象,然后去除父ResultMap中的構造器映射信息,將父ResultMap中配置的映射信息添加到當前ResultMap對象。
2.然后通過建造者模式在ResultMap.Builder類中創建一個ResultMap對象,然后為ResultMap對象的所有屬性賦值。
3.把ResultMap對象添加到Configuration對象的屬性resultMaps中,key為ResultMap對象的id,value為ResultMap對象。
二、映射實現原理:
StatementHandler組件與數據庫完成交互后,會使用ResultSetHandler組件對結果集進行處理。
PreparedStatementHandler類的query()方法:
1.調用PreparedStatement對象的execute()方法完成與數據庫交互,
2.調用ResultSetHandler對象的handleResultSets()方法對結果集進行處理。
ResultSetHandler接口只有一個默認的實現,即DefaultResultSetHandler類。
DefaultResultSetHandler類中handleResultSets()方法:
1.為了簡化對JDBC中ResultSet對象的操作,將ResultSet對象包裝成ResultSetWrapper對象,
2.然后獲取MappedStatement對象對應的ResultMap對象,接著調用重載的handleResultSet()方法,handleResultSet()方法中做了一些邏輯判斷,但最終都會調用DefaultResultSetHandler類的handleRowValues()方法。
在DefaultResultSetHandler類的handleRowValues()方法中處理結果集時,對嵌套的ResultMap和非嵌套ResultMap做了不同處理:
方法中判斷ResultMap中是否有嵌套的ResultMap,
當使用或標簽通過JOIN查詢方式進行級聯映射時,hasNestedResultMaps()方法的返回值為true,調用handleRowValuesForNestedResultMap()方法;
當使用和標簽關聯一個外部的查詢Mapper時,ResultMap對象的hasNestedResultMaps屬性值為false,調用handleRowValuesForSimpleResultMap()方法。
有嵌套ResultMap時的處理邏輯,handleRowValuesForNestedResultMap()方法:
對結果集對象進行遍歷,處理每一行數據。
首先調用resolveDiscriminatedResultMap()方法處理標簽中通過標簽配置的鑒別器信息,根據字段值獲取對應的ResultMap對象,
然后調用DefaultResultSetHandler類的getRowValue()方法將結果集中的一行數據轉換為Java實體對象。
在getRowValue()方法中:
1.調用createResultObject()方法處理通過<constructor>標簽配置的構造器映射,根據配置信息找到對應的構造方法,然后通過MyBatis中的ObjectFactory創建ResultMap關聯的實體對象。
2.調用applyAutomaticMappings()方法處理自動映射,對未通過<result>標簽配置映射的數據庫字段進行與Java實體屬性的映射處理。
方法中首先獲取未指定映射的所有數據庫字段和對應的Java屬性,然后獲取對應的字段值,通過反射機制為Java實體對應的屬性值賦值。
3.調用applyPropertyMappings()方法處理<result>標簽配置的映射信息。對所有<result>標簽配置的映射信息進行遍歷,然后找到數據庫字段對應的值,為Java實體屬性賦值。applyPropertyMappings()方法的實現代碼如下:
4.調用DefaultResultSetHandler類的applyNestedResultMappings()方法處理嵌套的結果集映射。applyNestedResultMappings()方法實現如下:
在applyNestedResultMappings()方法中:
1.首先獲取嵌套ResultMap對象,
2.然后根據嵌套ResultMap的Id從緩存中獲取嵌套ResultMap對應的Java實體對象,如果能獲取到,則調用linkObjects()方法將嵌套Java實體與外部Java實體進行關聯。
如果緩存中沒有,則調用getRowValue()方法創建嵌套ResultMap對應的Java實體對象并進行屬性映射,然后調用linkObjects()方法與外部的Java實體對象進行關聯。
沒有嵌套的ResultMap,handleRowValuesForSimpleResultMap()方法:
1.首先調用skipRows()方法跳過RowBounds對象指定偏移的行,
2.然后遍歷結果集中所有的行,對標簽配置的鑒別器進行處理,獲取實際映射的ResultMap對象,
3.接著調用getRowValue()方法處理一行記錄,將數據庫行記錄轉換為Java實體對象。getRowValue()方法實現如下:
在getRowValue():
1.創建ResultLoaderMap對象,該對象用于存放懶加載的屬性及對應的ResultLoader對象,MyBatis中的ResultLoader用于執行一個查詢Mapper,然后將執行結果賦值給某個實體對象的屬性。
2.調用createResultObject()方法創建ResultMap對應的Java實體對象,我們需要重點關注該方法的實現,代碼如下:
createResultObject()方法中:
1.首先調用重載的createResultObject()方法使用ObjectFactory對象創建Java實體對象,
2.然后判斷ResultMap中是否有嵌套的查詢,如果有嵌套的查詢并且開啟了懶加載機制,則通過MyBatis中的ProxyFactory創建實體對象的代理對象。
ProxyFactory接口有兩種不同的實現,分別為CglibProxyFactory和JavassistProxyFactory。
也就是說,MyBatis同時支持使用Cglib和Javassist創建代理對象,具體使用哪種策略創建代理對象,可以在MyBatis主配置文件中通過proxyFactory屬性指定。
3.調用applyAutomaticMappings()方法處理自動映射,
4.調用applyPropertyMappings()方法處理標簽配置的映射字段,該方法中除了為Java實體屬性設置值外,還將指定了懶加載的屬性添加到ResultLoaderMap對象中。
總結
以上是生活随笔為你收集整理的MyBatis 源码学习13——ResultMap的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 软件测试面试常用。
- 下一篇: 通过vi服务器运行vi,动态改变界面改变