MyBatis中在查詢進行select映射的時候,返回類型可以用resultType,也可以用resultMap,resultType是直接
表示返回類型的,而resultMap則是對外部ResultMap的引用,但是resultType跟resultMap不能同時存在。
1.resultType
?
在MyBatis進行查詢映射的時候,其實查詢出來的每一個屬性都是放在一個對應的Map里面的,其中鍵是屬性名,
值則是其對應的值。當提供的返回類型屬性是resultType的時候,MyBatis會將Map里面的鍵值對取出賦給
resultType所指定的對象對應的屬性。所以其實MyBatis的每一個查詢映射的返回類型都是ResultMap,
只是當我們提供的返回類型屬性是resultType的時候,MyBatis對自動的給我們把對應的值賦給resultType
所指定對象的屬性,而當我們提供的返回類型是resultMap的時候,因為Map不能很好表示領域模型,
我們就需要自己再進一步的把它轉化為對應的對象,這常常在復雜查詢中很有作用。
?
這里要強調的是,Mybatis是對返回的結果的每一行做映射的。所以,下面的語句返回的是Integer,而不是List
?
?
- <select?id="count"?parameterType="AreaDto"?resultType="java.lang.Integer">??
- ????????SELECT?id?FROM?USER??
- </select>??
?
返回一個int
?
- <select?id="count"?parameterType="AreaDto"?resultType="java.lang.Integer">??
- ????????SELECT?count(*)?FROM?USER??
- </select>??
?
返回map
- <select?id=”selectUsers”?parameterType=”int”?resultType=”hashmap”>??
- ????select?id,?username,?hashedPassword??
- ????from?some_table??
- ????where?id?=?#{id}??
- </select>??
?
這樣一個語句簡單作用于所有列被自動映射到HashMap的鍵上,這由resultType屬性指定。這在很多情況下是有用的,但是HashMap不能很好描述一個領域模型。那樣你的應用程序將會使用JavaBeans或POJOs(Plain Old Java Objects,普通Java對象)來作為領域模型
返回javaBEAN 對象
?
- <select?id="count"?parameterType="AreaDto"?resultType="User">??
- ????????SELECT?*?FROM?USER??
- </select>???
?
要記住類型別名是你的伙伴。使用它們你可以不用輸入類的全路徑。
?
- <typeAlias?type=”com.someapp.model.User”?alias=”User”/>??
?這些情況下,MyBatis會在幕后自動創建一個ResultMap,基于屬性名來映射列到JavaBean的屬性上
?
2.resultMap
?
MyBatis會自動創建一個ResultMap對象,然后基于查找出來的屬性名進行鍵值對封裝,然后再看到返回類型是Blog對象,再從ResultMap中取出與Blog對象對應的鍵值對進行賦值。
當返回類型直接是一個ResultMap的時候也是非常有用的,這主要用在進行復雜聯合查詢上,因為進行簡單查詢是沒有什么必要的。
?
簡單resultMap配置?
?
- <resultMap?type="com.liulanghan.Blog"?id="BlogResult">????
- ????<id?column="id"?property="id"/>????
- ????<result?column="title"?property="title"/>????
- ????<result?column="content"?property="content"/>????
- ????<result?column="owner"?property="owner"/>????
- </resultMap>???
- ???
- <select?id="selectBlog"?parameterType="int"?resultMap="BlogResult">????
- ??????select?*?from?t_blog?where?id?=?#{id}????
- </select>??
?
結果集的列比resultMap多會報錯么?
不會,只映射resultMap中有的列。
?
結果集的列比resultMap少會報錯么?
不會,只映射結果集中有的列。
?
高級結果映射
?
- <resultMap?id="detailedBlogResultMap"?type="Blog">??
- ????<constructor>??
- ????????<idArg?column="blog_id"?javaType="int"/>??
- ????</constructor>??
- ????<result?property="title"?column="blog_title"/>??
- ????<association?property="author"?column="blog_author_id"?javaType="?Author">??
- ????????<id?property="id"?column="author_id"/>??
- ????????<result?property="username"?column="author_username"/>??
- ????????<result?property="password"?column="author_password"/>??
- ????????<result?property="email"?column="author_email"/>??
- ????????<result?property="bio"?column="author_bio"/>??
- ????????<result?property="favouriteSection"?column="author_favourite_section"/>??
- ????</association>??
- ????<collection?property="posts"?ofType="Post">??
- ????????<id?property="id"?column="post_id"/>??
- ????????<result?property="subject"?column="post_subject"/>??
- ????????<association?property="author"?column="post_author_id"?javaType="Author"/>??
- ????????<collection?property="comments"?column="post_id"?ofType="?Comment">??
- ????????????<id?property="id"?column="comment_id"/>??
- ????????</collection>??
- ????????<collection?property="tags"?column="post_id"?ofType="?Tag"?>??
- ????????????<id?property="id"?column="tag_id"/>??
- ????????</collection>??
- ????????<discriminator?javaType="int"?column="draft">??
- ????????????<case?value="1"?resultType="DraftPost"/>??
- ????????</discriminator>??
- ????</collection>??
- </resultMap>??
?
?
resultMap
????? constructor – 類在實例化時,用來注入結果到構造方法中
?????????? idArg – ID參數;標記結果作為ID可以幫助提高整體效能
?????????? arg – 注入到構造方法的一個普通結果
????? id – 一個ID結果;標記結果作為ID可以幫助提高整體效能
????? result – 注入到字段或JavaBean屬性的普通結果
???? association – 一個復雜的類型關聯;許多結果將包成這種類型
????????? ?嵌入結果映射 – 結果映射自身的關聯,或者參考一個
???? collection – 復雜類型的集
?????????? 嵌入結果映射 – 結果映射自身的集,或者參考一個
??? discriminator – 使用結果值來決定使用哪個結果映射
???????? case – 基于某些值的結果映射
?????????????? 嵌入結果映射 – 這種情形結果也映射它本身,因此可以包含很多相同的元素,或者它可以參照一個外部的結果映射。
?
?
id 和result
???
id和result都映射一個單獨列的值到簡單數據類型
?
這兩者之間的唯一不同是id表示的結果將是當比較對象實例時用到的標識屬性。這幫助來改進整體表現,特別是緩存和嵌入結果映射(也就是聯合映射)。
?
它們共有的屬性如下:
?
property
?
映射到列結果的字段或屬性。如果匹配的是存在的,和給定名稱相同的JavaBeans的屬性,那么就會使用。否則MyBatis將會尋找給定名稱的字段。這兩種情形你可以使用通常點式的復雜屬性導航。比如,你可以這樣映射一些東西:“username”,或者映射到一些復雜的東西:“address.street.number”。
?
column
?
從數據庫中得到的列名,或者是列名的重命名標簽。這也是通常和會傳遞給resultSet.getString(columnName)方法參數中相同的字符串。
?
javaType
?
一個Java類的完全限定名,或一個類型別名(參加上面內建類型別名的列表)。如果你映射到一個JavaBean,MyBatis通常可以斷定類型。然而,如果你映射到的是HashMap,那么你應該明確地指定javaType來保證所需的行為。
?
jdbcType
?
在這個表格之后的所支持的JDBC類型列表中的類型。JDBC類型是僅僅需要對插入,更新和刪除操作可能為空的列進行處理。這是JDBC的需要,而不是MyBatis的。如果你直接使用JDBC編程,你需要指定這個類型-但僅僅對可能為空的值。
?
typeHandler
?
我們在前面討論過默認的類型處理器。使用這個屬性,你可以覆蓋默認的類型處理器。這個屬性值是類的完全限定名或者是一個類型處理器的實現,或者是類型別名。
constructor
?
。構造方法注入允許你在初始化時為類設置屬性的值,而不用暴露出公有方法。MyBatis也支持私有屬性和私有JavaBeans屬性來達到這個目的,但是一些人更青睞構造方法注入。
為了向這個構造方法中注入結果,MyBatis需要通過它的參數的類型來標識構造方法。Java沒有自查(反射)參數名的方法。所以當創建一個構造方法元素時,保證參數是按順序排列的,而且數據類型也是確定的。
?
association
?
association關聯元素處理“有一個”類型的關系,即一對一關聯。它有兩種關聯方式
?
嵌套查詢:通過執行另外一個SQL映射語句來返回預期的復雜類型。
?
嵌套結果:使用嵌套結果映射來處理重復的聯合結果的子集。
?
嵌套查詢
?
- <resultMap??id="userResultMap"?type="User">??
- ????<id?property="id"?column="ID"?jdbcType="NUMERIC"?javaType="java.lang.Long"/>??
- ????<result?property="loginName"?column="LOGIN_NAME"?jdbcType="VARCHAR"?javaType="java.lang.String"/>??
- ????<result?property="password"?column="password"?jdbcType="VARCHAR"?javaType="java.lang.String"/>??
- ????<result?property="roleId"?column="role_id"?jdbcType="NUMERIC"?javaType="java.lang.Long"/>??
- ????<association?property="role"?column="role_id"?javaType="Role"?select="selectRole"/>??
- </resultMap>??
- ??
- <select?id="selectUser"?parameterType="java.lang.Long"?resultMap="userResultMap"?>?????
- ????select?*?from?User?where?id?=#{id}?????
- </select>???
- ??
- <select?id="selectRole"?parameterType="java.lang.Long"?resultType="Role"?>?????
- ????select?*?from?Role?where?id?=#{id}?????
- </select>???
?
這里有兩個查詢,一個查詢加載User,一個查詢加載Role.
這里select為另外一個映射語句的ID,可以加載這個屬性映射需要的復雜類型。獲取的在列屬性中指定的列的值將被傳遞給目標select語句作為參數。
?
注意:
而select 為selectRole的SQL輸入參數可以隨便給名稱,只要是輸入參數與壓入進去的值類型相同就行了,可以寫成:
?
- select?*?from?Role?where?id?=?#{sfffs}???
?
不管輸入參數名稱是什么,mybatis最終會執行:
效果為:
?
- select?*?from?role?where?id?=resultSet.getLong("Role_id");??
?
注意:要保證第二個查詢查出來的結果只有一條記錄。
?
要處理復合主鍵,你可以指定多個列名通過column="{prop1=col1,prop2=col2}"這種語法來傳遞給嵌套查詢語句。這會引起prop1和prop2以參數對象形式來設置給目標嵌套查詢語句。
- <resultMap??id="userResultMap"?type="User">??
- ????<id?property="id"?column="ID"?jdbcType="NUMERIC"?javaType="java.lang.Long"/>??
- ????<result?property="loginName"?column="LOGIN_NAME"?jdbcType="VARCHAR"?javaType="java.lang.String"/>??
- ????<result?property="password"?column="password"?jdbcType="VARCHAR"?javaType="java.lang.String"/>??
- ????<result?property="roleId"?column="role_id"?jdbcType="NUMERIC"?javaType="java.lang.Long"/>??
- ????<association?property="role"?column="{id=role_id,name=role_name}"?javaType="Role"?select="selectRole"/>??
- </resultMap>??
- ??
- <select?id="selectRole"?parameterType="HashMap"??resultType="Role"?>?????
- ????select?*?from?Role?where?id?=#{id}?and?name=?#{name}?????
- </select>??
??
這種方式很簡單,但是對于大型數據集合和列表將不會表現很好。問題就是我們熟知的“N+1查詢問題”。概括地講,N+1查詢問題可以是這樣引起的:
?? 你執行了一個單獨的SQL語句來獲取結果列表(就是“+1”)。
?? 對返回的每條記錄,你執行了一個查詢語句來為每個加載細節(就是“N”)。
這個問題會導致成百上千的SQL語句被執行。這通常不是期望的。
比如一個查詢用戶列表的SQL,假如有2000個用戶,那么就是一個查詢用戶的SQL和2000個查詢角色的SQL,一共有2001個SQL被運行。
MyBatis能延遲加載這樣的查詢就是一個好處,因此你可以分散這些語句同時運行的消耗。然而,如果你加載一個列表,之后迅速迭代來訪問嵌套的數據,你會調用所有的延遲加載,這樣的行為可能是很糟糕的。
?
所以還有另外一種方法。
關聯的嵌套結果
?
嵌套結果
?
- <resultMap??id="userResultMap"?type="User">??
- ????<id?property="id"?column="ID"?jdbcType="NUMERIC"?javaType="java.lang.Long"/>??
- ????<result?property="loginName"?column="LOGIN_NAME"?jdbcType="VARCHAR"?javaType="java.lang.String"/>??
- ????<result?property="password"?column="password"?jdbcType="VARCHAR"?javaType="java.lang.String"/>??
- ????<result?property="roleId"?column="role_id"?jdbcType="NUMERIC"?javaType="java.lang.Long"/>??
- ????<association?property="role"?column="role_id"?javaType="Role"?resultMap="roleResultMap"/>??
- ????????<id?property="id"?column="role_id"/>??
- ????????<result?property="name"?column="role_name"/>??
- ????</association>??
- </resultMap>??
- ??
- <resultMap?id="roleResultMap"?type="Role">??
- ????<id?property="id"?column="role_id"/>??
- ????<result?property="name"?column="role_name"/>??
- </resultMap>??
?
?
也可以這樣配置
?
- <resultMap??id="userResultMap"?type="User">??
- ????<id?property="id"?column="ID"?jdbcType="NUMERIC"?javaType="java.lang.Long"/>??
- ????<result?property="loginName"?column="LOGIN_NAME"?jdbcType="VARCHAR"?javaType="java.lang.String"/>??
- ????<result?property="password"?column="password"?jdbcType="VARCHAR"?javaType="java.lang.String"/>??
- ????<result?property="roleId"?column="role_id"?jdbcType="NUMERIC"?javaType="java.lang.Long"/>??
- ????<association?property="role"?column="role_id"?javaType="Role"?resultMap="roleResultMap"/>??
- </resultMap>??
- ??
- <resultMap?id="roleResultMap"?type="Role">??
- ????<id?property="id"?column="role_id"/>??
- ????<result?property="name"?column="role_name"/>??
- </resultMap>??
?
?resultMap這是結果映射的ID,可以映射關聯的嵌套結果到一個合適的對象圖中。這是一種替代方法來調用另外一個查詢語句。這允許你聯合多個表來合成到一個單獨的結果集。這樣的結果集可能包含重復,數據的重復組需要被分解,合理映射到一個嵌套的對象圖。為了使它變得容易,MyBatis讓你“鏈接”結果映射,來處理嵌套結果。一個例子會很容易來仿照,這個表格后面也有一個示例。
?
注意這個聯合查詢,以及采取保護來確保所有結果被唯一而且清晰的名字來重命名。
?
columnPrefix 屬性
?
- <association?property="role"?column="role_id"?javaType="Role"?resultMap="roleResultMap"?columnPrefix="role_"/>??
- ????????<id?property="id"?column="id"/>??
- ????????<result?property="name"?column="name"/>??
- </association>??
?
非常重要:在嵌套據誒過映射中id元素扮演了非常重要的角色。應應該通常指定一個或多個屬性,它們可以用來唯一標識結果。實際上就是如果你離開她了,但是有一個嚴重的性能問題時MyBatis仍然可以工作。選擇的屬性越少越好,它們可以唯一地標識結果。主鍵就是一個顯而易見的選擇(盡管是聯合主鍵)。
?
上面你已經看到了如何處理“有一個”類型關聯。但是“有很多個”是怎樣的?下面這個部分就是來討論這個主題的。
?
collection
?
collection關聯元素處理一對多關聯。
?
- <resultMap??id="roleResultMap"?type="Role">??
- ????<id?property="id"?column="ID"?jdbcType="NUMERIC"?javaType="java.lang.Long"/>??
- ????<result?property="name"?column="NAME"?jdbcType="VARCHAR"?javaType="java.lang.String"/>??
- ????<result?property="userId"?column="user_id"?jdbcType="NUMERIC"?javaType="java.lang.Long"/>??
- ????<collection?property="user"?column="user_id"?javaType="ArrayList"?ofType="Post"?select="selectUser"/>??
- ??
- </resultMap>??
- ??
- <select?id="selectUser"?parameterType="java.lang.Long"???resultType="User"?>?????
- ????select?*?from?uer?where?id?=#{id}???
- </select>???
?
同樣,可以這樣配置
?
- <resultMap??id="roleResultMap"?type="Role">??
- ????<id?property="id"?column="ID"?jdbcType="NUMERIC"?javaType="java.lang.Long"/>??
- ????<result?property="name"?column="NAME"?jdbcType="VARCHAR"?javaType="java.lang.String"/>??
- ????<result?property="userId"?column="user_id"?jdbcType="NUMERIC"?javaType="java.lang.Long"/>??
- ????<collection?property="user"?column="user_id"?javaType="ArrayList"?ofType="Post">??
- ????????<id?property="id"?column="user_id"/>??
- ????????<result?property="name"?column="user_name"/>??
- ????</collection>??
- </resultMap>??
- <resultMap?id="userResultMap"?type="User">??
- ????<id?property="id"?column="user_id"/>??
- ????<result?property="name"?column="user_name"/>??
- </resultMap>??
??
?