了解iBatis.Net中的ResultMap
我們將來討論一下在ibatis中非常重要的一個(gè)內(nèi)容,在我個(gè)人看來,能否真正用好ibatis的一個(gè)關(guān)鍵,這就是ResultMap。字面上理解,它就是結(jié)果集的映射,就是將返回的記錄逐個(gè)字段的映射賦值給對(duì)象的屬性上。其實(shí)如果沒有特殊需求的話我們完全可以使用ResultClass來代替它,因?yàn)槿绻侄闻c屬性一模一樣的話,查詢出來數(shù)據(jù)集會(huì)自動(dòng)匹配到ResultClass指定的類的實(shí)例對(duì)象,如果字段名不在屬性中的話,那這個(gè)字段將不會(huì)被返回的實(shí)例體類對(duì)象接受,相當(dāng)于沒有查詢出這個(gè)字段一樣的。
每個(gè)ResultMap都有一個(gè)自己的ID,如果你在sqlmap.config中沒有配置使用命名空間的話,那么這個(gè)ResulteMap ID是全局(這點(diǎn)在所有的ibatis配置元素都是一樣的),ResultMap一個(gè)重要的屬性的是class,它將決定這個(gè)ResultMap對(duì)應(yīng)的實(shí)例的類,換句話講,它的作用是指出結(jié)果集要映射的數(shù)據(jù)類型。在extends屬性中可以設(shè)置它將要繼承的ResultMap,如果給他指定的了值,那么它將會(huì)從super Resultmap繼承所的映射配置字段。定義如下:
<resultMaps>
<resultMap id="DemoResultMap" class="Hashtable">
</resultMap>
</resultMaps>
如果你有正確配置了ibatis的XSD架構(gòu)文件的話,那么這時(shí)候就會(huì)提示resultMap的定義是不完全的。沒錯(cuò),接下來就是要定義Result元素。每一個(gè)result元素都是定義一個(gè)字段與數(shù)據(jù)類屬性對(duì)應(yīng)的映射。在每一個(gè)result元素有比較多的屬性參數(shù),其中property和column是必須的,其它的參數(shù)屬性都是可選的。所以我們?cè)诿恳粋€(gè)resultMap中必須定義超過一個(gè)以上的result定義。通常以下的配置就可以完成基本的配置了。
<resultMaps>
<resultMap id="DemoResultMap" class="Hashtable">
<result property="id" column="id"/>
</resultMap>
</resultMaps>
但如果你需要更多的要求的話,result map仍然能夠最大限度的滿足你。columnIndex屬性提供了我們將數(shù)據(jù)集的第幾個(gè)下標(biāo)字段映射到指定的數(shù)據(jù)對(duì)象屬性的方案,但是這種方式應(yīng)該盡量的少用,你會(huì)發(fā)現(xiàn)這對(duì)我們以后的維護(hù)和可讀性會(huì)產(chǎn)生很大的副作用。dbType屬性明確指出這個(gè)字段對(duì)應(yīng)的數(shù)據(jù)庫的類型,大多數(shù)情況我很少會(huì)用到。type屬性則明確指出這個(gè)字段將對(duì)應(yīng)的數(shù)據(jù)對(duì)象屬性的數(shù)據(jù)類型,通常如果你想保證類型安全的話,設(shè)置這個(gè)屬性是很必要的。resultMapping屬性則稍微復(fù)雜一些,它是用在一種場景下,如果一個(gè)數(shù)據(jù)類的屬性本身不是基元數(shù)據(jù)類型,而是一個(gè)復(fù)雜數(shù)據(jù)類型的話,那我們就不可能很簡單地給它一個(gè)簡單的result元素就了事了,還必須給他一個(gè)完整的resultMap。而resultMapping屬性就是為了完成這個(gè)功能而存在的。它的屬性值是一個(gè)已存在的resultMap的ID。nullValue屬性就沒什么好講的了,它是給出當(dāng)這個(gè)字段的值為null的時(shí)候,它的默認(rèn)值是多少。select屬性同resultMapping一樣比較復(fù)雜一樣,先說一下它的屬性值必須是一個(gè)返回?cái)?shù)據(jù)集合的查詢語句的ID,能配置這個(gè)屬性的數(shù)據(jù)類屬性可以是一個(gè)基元類型,復(fù)合類型,也可以是一個(gè)包括多條數(shù)據(jù)的集合類型,這些類型都行,沒有問題的。它的一處重要的存在意義就在于描述不同表之間的關(guān)系問題,通過本次的查詢,你想不通過join的手段從另一個(gè)表查詢相關(guān)字段的時(shí)候,你就可以使用select屬性。如下:
<resultMaps> <resultMap id="DemoResultMap" class="Hashtable"> <result property="id" column="id"/> <result property="Children" column="id" select="SELECT_Children"/> </resultMap> </resultMaps> <statements> <select id="SELECT_Children" resultClass="ChildrenObject"> SELECT * FROM Children WHERE ParentID = #id# </select> </statements>
這樣就可以做到不用通過編程的方式來表示不同表的關(guān)聯(lián)關(guān)系和數(shù)據(jù)讀取問題。但是這樣有可能存在一種問題,如果你每次都要讀取數(shù)據(jù)的時(shí)候,你會(huì)發(fā)現(xiàn)你會(huì)產(chǎn)生更多次的與數(shù)據(jù)庫交互的情況,并且即使你不是每次都需要這數(shù)據(jù),那會(huì)不會(huì)造成數(shù)據(jù)讀取的浪費(fèi)呢?接下來的lazyLoad屬性就為我們提供了第二種問題的解決方案了,那就是數(shù)據(jù)的延遲加載,沒錯(cuò),延遲加載可以大大改善數(shù)據(jù)訪問的性能,它只是要需的時(shí)候才去讀取這些數(shù)據(jù),對(duì)于主從表關(guān)系的時(shí)候,這樣的方式可能是最好的解決方式了。
OK,關(guān)于ResultMap的介紹就先到此為止,接下來我要記錄一下,我在使用過程中遇到的一些問題:
一.在使用ResultMap的時(shí)候,你要特別注意,如果你在ResultMap中給出的配置字段,但是你返回的數(shù)據(jù)集的時(shí)候卻沒有返回這個(gè)字段,那程序?qū)⒊鰭伋霎惓!5窍喾吹?,如果你返回了一些字段,卻沒有在ResultMap給出配置定義的話,那么那些字段將不會(huì)被處理而不會(huì)給你任何的提示,相當(dāng)沒有查詢出這些字段。你要特別注意這個(gè)問題。
二.如果沒有特別需求的情況,我建議還是把數(shù)據(jù)類的屬性設(shè)計(jì)成與數(shù)據(jù)庫字段字一樣的比較,這樣如果一般情況下我們都可以不用寫這個(gè)ResultMap,事實(shí)上如果沒有這樣的特殊要求,那么去寫這個(gè)ResultMap仍然是一件非常耗時(shí),并且容易出錯(cuò)的一份差事。
三.在使用lazyLoad的時(shí)候要特別注意,不是什么類型的數(shù)據(jù)都可以lazyLoad的,只有是實(shí)現(xiàn)的IList的接口的類型,并且數(shù)據(jù)類的屬性定義為IList類型的字段才能被lazyLoad。(關(guān)于是否只有IList類型的屬性才能被lazyLoad的問題還需要探討一下,因?yàn)榫臀沂褂玫慕?jīng)驗(yàn)只有這種類型才可以,甚至是Generic版的IList都不支持)。而且你在使用它的時(shí)候,還不能把這個(gè)IList類型的屬性轉(zhuǎn)換成你真正的數(shù)據(jù)類型。因?yàn)樵谶\(yùn)行時(shí),這個(gè)屬性會(huì)被包裝成一個(gè)動(dòng)態(tài)的類型,這個(gè)動(dòng)態(tài)類型仍然實(shí)現(xiàn)了IList接口,就是因?yàn)檫@個(gè)動(dòng)態(tài)類型才擴(kuò)展了我們可以lazyLoad的功能。這時(shí)候在程序中使用的是運(yùn)行時(shí)的動(dòng)態(tài)類型所以你沒辦法進(jìn)行強(qiáng)類型轉(zhuǎn)換。
問題暫時(shí)沒有想到更多了,如果以后還有關(guān)于resultMap的問題,我都會(huì)更新上來,也希望大家一起來指正我的一些錯(cuò)誤和不足,一起完善。不要讓我的一些錯(cuò)誤的實(shí)踐誤導(dǎo)了初學(xué)者,謝謝。
【編輯推薦】