[MyBatis]-resultMap結果對映集詳解
resultMap結果對映集詳解
resultmap是mybatis中最複雜的元素之一,它描述如何從結果集中載入物件,主要作用是定義對映規則、級聯的更新、定製型別轉化器。
resultmap構成元素
元素 | 子元素 | 作用 |
---|---|---|
constructor | idArg 、arg | 用於配置構造器方法 |
id | 將結果集標記為id,以方便全域性呼叫 | |
result | 配置POJO到資料庫列名對映關係 | |
association | 級聯使用 | 代表一對一關係 |
collection | 級聯使用 | 代表一對多關係 |
discriminator | 級聯使用 | 鑑別器 根據實際選擇例項,可以通過特定條件確定結果集 |
1.id和result元素
以User類為例:
class User{ private int userId; private String name; public User(long userId,String name){ this.userId = userId; this.name = name; } }
- id、result是最簡單的對映,id為主鍵對映;result其他基本資料庫表字段到實體類屬性的對映。resultmap的xml如下:
<resultMap type="User" id="userMap"> <id property="UserId" column="user_id" javaType="int" jdbcType="int"/> <result property="name" column="name" javaType="String" jdbcType="VARCHAR"/> </resultMap>
id、result語句屬性配置細節:
屬性 | 描述 |
---|---|
property | 需要對映到JavaBean 的屬性名稱。 |
column | 資料表的列名或者標籤別名。 |
javaType | 一個完整的類名,或者是一個類型別名。如果你匹配的是一個JavaBean,那MyBatis 通常會自行檢測到。然後,如果你是要對映到一個HashMap,那你需要指定javaType 要達到的目的。 |
jdbcType | 資料表支援的型別列表。這個屬性只在insert,update 或delete 的時候針對允許空的列有用。JDBC 需要這項,但MyBatis 不需要。如果你是直接針對JDBC 編碼,且有允許空的列,而你要指定這項。 |
typeHandler | 使用這個屬性可以覆寫型別處理器。這項值可以是一個完整的類名,也可以是一個類型別名。 |
2. CONSTRUCTOR 構造器
在resultMap中,通常使用id、result子元素把Java實體類的屬性對映到資料庫表的欄位上。但是如果在遇到JavaBean沒有無參建構函式時,我還需要使用構造器元素實現一個JavaBean的例項化和資料注入。
再以User為例,那麼對應的resultmap就需要新增構造器:
<constructor>
<idArg column="user_id" javaType="long"/>
<arg column="name" javaType="String"/>
</constructor>
- 定義java實體類的屬性對映到資料庫表的欄位上。我們也可以使用實體類的構造方法來實現值的對映,這是通過構造方法引數的書寫的順序來進行賦值的。
這樣MyBatis就知道需要用這個構造方法構造了。
3.結果集處理方法
1. 使用map儲存結果集
一般情況下,所有select語句都可以使用map儲存,但是使用map就意味著可讀性的下井,所以這不是推薦的方式。
<select id="findUserById" parameterType="long" resultType="map">
select user_id ,name form user where user_id=#{userId}
</select>
2. 使用POJO儲存結果集(推薦)
一般我們都使用POJO儲存查詢結果。我們可以使用select自動對映,還可以使用select語句中的resultMap屬性配置對映集合,不過需要提前定義resultMap。
那我們就可以將之前的select語句修改:
<select id="findUserById" parameterType="long" resultMap="userMap">
select user_id ,name form user where user_id=#{userId}
</select>
4.級聯
在資料庫中包含著一對多、一對一的關係。比如說一個人和他的身份證就是一對一的關係,但是他和他的銀行卡就是一對多的關係。我們的生活中存在著很多這樣的場景。我們也希望在獲取這個人的資訊的同時也可以把他的身份證資訊一同查出,這樣的情況我們就要使用級聯。在級聯中存在3種對應關係。
- 一對一的關係
- 一對多的關係
- 多對多的關係(這種情況由於比較複雜,我們通常會使用雙向一對多的關係來降低複雜度)
1.association 一對一級聯
我們繼續使用User類,同時為其增加一個Card類,人和他的身份證就行成了一對一的關係。我們再建立一個Card類。
@Data
@Alias("card")
public class Card {
private Long id;
private Long userId;
private String name;
private String address;
}
這是需要在User中新增屬性Card,這樣就形成了一對一的級聯。
@Data
@Alias("user")
public class User {
private Long id;
private String username;
private String password;
private String email;
private Card card;}
這裡使用了Lombok的Data註解省去了get和set等方法。使用Alias註解新增其別名。
Lombok 是一種 Java™ 實用工具,可用來幫助開發人員消除 Java 的冗長,尤其是對於簡單的 Java 物件(POJO)。它通過註解實現這一目的。以後會新增其使用方法的文章。
這時需要CardMapper提供findCardByUserId(Long userId)方法,定義其對映器如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.changzhen.mybatis.mapper.CardMapper">
<resultMap id="cardMap" type="card">
<id property="id" column="id"></id>
<result property="userId" column="user_id"/>
<result property="name" column="name"/>
<result property="address" column="address"/>
</resultMap>
<select id="findCardByUserId" parameterType="long" resultMap="cardMap">
SELECT id, user_id, name, address FROM card WHERE user_id = #{userId}
</select>
</mapper>
有了CardMapper,我們將可以在UserMaper中使用 findCardByUserId
進行級聯
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.changzhen.mybatis.mapper.UserMapper">
<select id="getUser" resultMap="userMap" parameterType="long">
SELECT id,username,password,email FROM USER WHERE id=#{id}
</select>
<resultMap id="userMap" type="user">
<id property="id" column="id"/>
<result property="password" column="password"/>
<result property="email" column="email"/>
<association property="card" column="id" select="com.changzhen.mybatis.mapper.CardMapper.findCardByUserId"/>
</resultMap>
注意在getUser的時候,一定要將資料庫的id獲取到,開始我以為在傳入id的時候就已經把id傳入findCardByUserId的userId了,結果怎麼都獲取不到Card。後來發現原來是根據返回的id進而級聯查詢。
通過關聯處理,其中select元素指定的sql查詢,而column則是指定傳遞給select的引數,是user物件id。當取出User的時候,MyBatis就知道下面的sql取出我們需要的級聯資訊。
<association property="card" column="id" select="com.changzhen.mybatis.mapper.CardMapper.findCardByUserId"/>
其中引數是User的值,通過column配置,如果是多個引數則使用逗號隔開。
通過測試
可以看出執行了兩條sql分別查詢了User和Card。
2.collection 一對多級聯
這一對多的級聯,一個身份證可以辦理多張銀行卡。每個使用者對應一個身份證,每個身份證對應多個銀行卡。所以這兩個級聯,分別使用association和collection完成。
- 首先,建立BankCard類,為Card增加bankCardList屬性
@Alias("bankCard")
@Data
public class BankCard {
private Long id;
private Long userId;
private String bankName;
private int type;
}
@Data
@Alias("card")
public class Card {
private Long id;
private Long userId;
private String name;
private String address;
private List<BankCard> bankCards;
}
-
建立BankCardMapper
@Mapper
public interface BankCardMapper {
public List findCreditCardsByUserId(Long userId);
} -
xml最簡單的對映器
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.changzhen.mybatis.mapper.CreditCardMapper">
<select id="findCreditCardsByUserId" parameterType="long" resultType="creditCard">
SELECT id, user_id, bank_name, type FROM bank_card WHERE user_id = #{userId}
</select>
</mapper>
- 最後在Card對映器中新增collection,一對多級聯。
<mapper namespace="com.changzhen.mybatis.mapper.CardMapper">
<resultMap id="cardMap" type="card">
<id property="id" column="id"></id>
<result property="userId" column="user_id"/>
<result property="name" column="name"/>
<result property="address" column="address"/>
<collection property="creditCards" column="id" select="com.changzhen.mybatis.mapper.BankCardMapper.findBankCardsByUserId"/>
</resultMap>
<select id="findCardByUserId" parameterType="long" resultMap="cardMap">
SELECT id, user_id, name, address FROM card WHERE user_id = #{userId}
</select>
</mapper>
-通過測試
可以看出共執行了三條sql語句。
3.discriminator 鑑別器級聯
鑑別器級聯是在不公情況下使用不同的POJO。例如,在本例中我們每個人都有好多銀行卡,但是銀行卡的種類卻不同,比如有借記卡(DebitCard)和信用卡(CreditCard),我們需要按需來建立不同的POJO。這時我們就需要在BankCard中新增types屬性進行判斷,確定使用哪個POJO。
接下來需要建立DebitCard和CreditCard兩個繼承BankCard的子類
@Data
public class CreditCard extends BankCard {
/**
* 消費額度
*/
private String creditLine;
}
@Data
public class DebitCard extends BankCard {
/**
* 存款金額
*/
private String deposit;
}
通過BankCard中types的屬性判斷使用哪種銀行卡,例如types等於1為借記卡,等於2為信用卡。接下來需要修改bankCard.xml,新增discriminator鑑別器,它相當於java中的switch語句。
bankCard.xml修改為如下:
<mapper namespace="com.changzhen.mybatis.mapper.BankCardMapper">
<resultMap id="bankCardMapper" type="bankCard">
<id property="id" column="id"/>
<result property="userId" column="user_id"/>
<result property="bankName" column="bank_name"/>
<result property="types" column="types"/>
<discriminator javaType="int" column="types">
<case value="1" resultMap="debitCardMapper"></case>
<case value="2" resultMap="creditCardMapper"></case>
</discriminator>
</resultMap>
<select id="findBankCardsByUserId" parameterType="long" resultMap="bankCardMapper">
SELECT id, user_id, bank_name, types FROM bank_card WHERE user_id = #{userId}
</select>
<resultMap id="debitCardMapper" type="debitCard" extends="bankCardMapper"/>
<resultMap id="creditCardMapper" type="creditCard" extends="bankCardMapper"/>
其中
<discriminator javaType="int" column="types">
<case value="1" resultMap="debitCardMapper"></case>
<case value="2" resultMap="creditCardMapper"></case>
</discriminator>
首先定義了 discriminator ,它對應的column為types,java型別(jdbcType)為int。
case配置了不同的resultMap,如value=1時,引入debitCardMapper,value=2時
,引入creditCardMapper他們都擴充套件了bankCardMapper。
就像繼承關係一樣,resultMap也可以繼承,加入自己的屬性。借記卡新增deposit屬性,信用卡新增creditLine屬性。
-通過執行測試
由圖可以看出,返回的資料已經根據types使用了不同的POJO。