關聯 association
關聯(association)元素處理“有一個”類型的關系。 比如,在我們的示例中,一個博客有一個用戶。關聯結果映射和其它類型的映射工作方式差不多。 你需要指定目標屬性名以及屬性的javaType
(很多時候 MyBatis 可以自己推斷出來),在必要的情況下你還可以設置 JDBC 類型,如果你想覆蓋獲取結果值的過程,還可以設置類型處理器。
關聯的不同之處是,你需要告訴 MyBatis 如何加載關聯。MyBatis 有兩種不同的方式加載關聯:
- 嵌套 Select 查詢:通過執行另外一個 SQL 映射語句來加載期望的復雜類型。
- 嵌套結果映射:使用嵌套的結果映射來處理連接結果的重復子集。
首先,先讓我們來看看這個元素的屬性。你將會發現,和普通的結果映射相比,它只在 select 和 resultMap 屬性上有所不同。
屬性 | 描述 |
---|---|
property |
映射到列結果的字段或屬性。如果用來匹配的 JavaBean 存在給定名字的屬性,那么它將會被使用。否則 MyBatis 將會尋找給定名稱的字段。 無論是哪一種情形,你都可以使用通常的點式分隔形式進行復雜屬性導航。 比如,你可以這樣映射一些簡單的東西:“username”,或者映射到一些復雜的東西上:“address.street.number”。 |
javaType |
一個 Java 類的完全限定名,或一個類型別名(關於內置的類型別名,可以參考上面的表格)。 如果你映射到一個 JavaBean,MyBatis 通常可以推斷類型。然而,如果你映射到的是 HashMap,那么你應該明確地指定 javaType 來保證行為與期望的相一致。 |
jdbcType |
JDBC 類型,所支持的 JDBC 類型參見這個表格之前的“支持的 JDBC 類型”。 只需要在可能執行插入、更新和刪除的且允許空值的列上指定 JDBC 類型。這是 JDBC 的要求而非 MyBatis 的要求。如果你直接面向 JDBC 編程,你需要對可能存在空值的列指定這個類型。 |
typeHandler |
我們在前面討論過默認的類型處理器。使用這個屬性,你可以覆蓋默認的類型處理器。 這個屬性值是一個類型處理器實現類的完全限定名,或者是類型別名。 |
關聯的嵌套 Select 查詢
屬性 | 描述 |
---|---|
column |
數據庫中的列名,或者是列的別名。一般情況下,這和傳遞給 resultSet.getString(columnName) 方法的參數一樣。 注意:在使用復合主鍵的時候,你可以使用 column="{prop1=col1,prop2=col2}" 這樣的語法來指定多個傳遞給嵌套 Select 查詢語句的列名。這會使得 prop1 和 prop2 作為參數對象,被設置為對應嵌套 Select 語句的參數。 |
select |
用於加載復雜類型屬性的映射語句的 ID,它會從 column 屬性指定的列中檢索數據,作為參數傳遞給目標 select 語句。 具體請參考下面的例子。注意:在使用復合主鍵的時候,你可以使用 column="{prop1=col1,prop2=col2}" 這樣的語法來指定多個傳遞給嵌套 Select 查詢語句的列名。這會使得 prop1 和 prop2 作為參數對象,被設置為對應嵌套 Select 語句的參數。 |
fetchType |
可選的。有效值為 lazy 和 eager 。 指定屬性后,將在映射中忽略全局配置參數 lazyLoadingEnabled ,使用屬性的值。 |
就是這么簡單。我們有兩個 select 查詢語句:一個用來加載博客(Blog),另外一個用來加載作者(Author),而且博客的結果映射描述了應該使用 selectAuthor
語句加載它的 author 屬性。
其它所有的屬性將會被自動加載,只要它們的列名和屬性名相匹配。
這種方式雖然很簡單,但在大型數據集或大型數據表上表現不佳。這個問題被稱為“N+1 查詢問題”。 概括地講,N+1 查詢問題是這樣子的:
- 你執行了一個單獨的 SQL 語句來獲取結果的一個列表(就是“+1”)。
- 對列表返回的每條記錄,你執行一個 select 查詢語句來為每條記錄加載詳細信息(就是“N”)。
這個問題會導致成百上千的 SQL 語句被執行。有時候,我們不希望產生這樣的后果。
好消息是,MyBatis 能夠對這樣的查詢進行延遲加載,因此可以將大量語句同時運行的開銷分散開來。 然而,如果你加載記錄列表之后立刻就遍歷列表以獲取嵌套的數據,就會觸發所有的延遲加載查詢,性能可能會變得很糟糕。
<resultMap id ="bookMap2" type="Book">
<id property="id" column="id" />
<result property="bookName" column="book_name" />
<result property="authorId" column="author_id" />
<association property="author" javaType="Author" column="author_id"
select="com.wcw.mapper.AuthorMapper.selectById">
</association>
</resultMap>
<select id ="selectBookDetail2" resultMap="bookMap2">
select * from book where id = #{bookId}
</select>
<resultMap id = "authorMap1" type="Author">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="password" column="password "/>
<result property="email" column="email"/>
<result property="bio" column="bio"/>
</resultMap>
<select id="selectById" resultMap="authorMap1">
select
a.id,a.username,a.password,email,a.bio
from author a where a.id = #{id}
</select>
關聯的嵌套結果映射
屬性 | 描述 |
---|---|
resultMap |
結果映射的 ID,可以將此關聯的嵌套結果集映射到一個合適的對象樹中。 它可以作為使用額外 select 語句的替代方案。它可以將多表連接操作的結果映射成一個單一的 ResultSet 。這樣的 ResultSet 有部分數據是重復的。 為了將結果集正確地映射到嵌套的對象樹中, MyBatis 允許你“串聯”結果映射,以便解決嵌套結果集的問題。使用嵌套結果映射的一個例子在表格以后。 |
columnPrefix |
當連接多個表時,你可能會不得不使用列別名來避免在 ResultSet 中產生重復的列名。指定 columnPrefix 列名前綴允許你將帶有這些前綴的列映射到一個外部的結果映射中。 詳細說明請參考后面的例子。 |
notNullColumn |
默認情況下,在至少一個被映射到屬性的列不為空時,子對象才會被創建。 你可以在這個屬性上指定非空的列來改變默認行為,指定后,Mybatis 將只在這些列非空時才創建一個子對象。可以使用逗號分隔來指定多個列。默認值:未設置(unset)。 |
autoMapping |
如果設置這個屬性,MyBatis 將會為本結果映射開啟或者關閉自動映射。 這個屬性會覆蓋全局的屬性 autoMappingBehavior。注意,本屬性對外部的結果映射無效,所以不能搭配 select 或 resultMap 元素使用。默認值:未設置(unset)。 |
<resultMap id ="bookMap" type="Book">
<id property="id" column="id" />
<result property="bookName" column="book_name" />
<association property="author" javaType="Author" columnPrefix="author_"
resultMap="com.wcw.mapper.AuthorMapper.authorMap1">
</association>
</resultMap>
<select id="selectBookDetail" resultMap="bookMap" >
select b.id as id,
b.book_name,
b.author_id,
a.username as author_username,
a.password as author_password,
a.email as author_email,
a.bio as author_bio
from book b inner join author a on b.author_id = a.id
where b.id = #{bookId}
</select>