MyBatis實現一對一關聯映射

在現實生活中,一對一關聯關係是十分常見的。例如,一個人只能有一個身份證,同時一個身份證也只會對應一個人。

那麼使用MyBatis是怎麼處理這種一對一關聯關係的呢?在前面講解的<resultMap>元素中,包含了一個<association>子元素,MyBatis就是通過該元素來處理一對一關聯關係的。

<association>元素中,通常可以配置以下屬性。

  • property:指定映射到的實體類對象屬性,與表字段一一對應。
  • column:指定表中對應的字段。
  • javaType:指定映射到實體對象屬性的類型。
  • select:指定引入嵌套查詢的子SQL語句,該屬性用於關聯映射中的嵌套查詢。
  • fetchType:指定在關聯查詢時是否啓用延遲加載。fetchType屬性有lazy和eager兩個屬性值,默認值爲lazy(即默認關聯映射延遲加載)。

<association>元素的使用非常簡單,只需要參考如下兩種示例配置即可,具體如下。

<!-- 方式一:嵌套查詢-->
  <resultMap type="IdCard" id="IdCardById">
		<id property="id" column="id" />
		<result property="userName" column="userName" />
		<!-- 一對一:association使用select屬性引入另外一條SQL語句 -->
		<association property="user" column="uid" javaType="User"
			select="cn.dsscm.dao.UserMapper.findUserById" />
   </resultMap>
<!-- 方式二:嵌套結果 -->
	<resultMap type="IdCard" id="userRoleResult2">
		<id property="id" column="id"/>
		<result property="code" column="code"/>
		<association property="user" javaType="User">
			<id property="id" column="cid"/>
			<result property="userName" column="userName" />
		</association>
	</resultMap>

MyBatis在映射文件中加載關聯關係對象主要通過兩種方式:嵌套查詢嵌套結果。嵌套查詢是指通過執行另外一條SQL映射語句來返回預期的複雜類型;嵌套結果是使用嵌套結果映射來處理重複的聯合結果的子集。開發人員可以使用上述任意一種方式實現對關聯關係的加載。

示例:用戶和身份證間關聯

瞭解了MyBatis中處理一對一關聯關係的元素和方式後,接下來就以用戶和身份證之間的一對一關聯關係爲例,進行詳細講解。
查詢個人及其關聯的身份證信息是先通過查詢個人表中的主鍵來獲個人信息,然後通過表中的外鍵,來獲取證件表中的身份證號信息。其具體實現步驟如下。
創建數據表,在dsscm數據庫中重新創建名爲tb_idcard的數據表,同時預先插入兩條數據。其執行的SQL語句如下所示。

USE dsscm;
#創建一個名稱爲tb idcard 的表
CREATE TABLE `tb_idcard` (
   `id` int(11) NOT NULL AUTO_INCREMENT,
   `uid` int(11) NOT NULL,
   `CODE` varchar(18) DEFAULT NULL,
   PRIMARY KEY (`id`)
 );
#插入n條數據
INSERT INTO tb_idcard(uid ,CODE) VALUES (1,'430101200001011234');
INSERT INTO tb_idcard(uid ,CODE) VALUES (2,'430101200001014321');
INSERT INTO tb_idcard(uid ,CODE) VALUES (3,'430101200001011235');
INSERT INTO tb_idcard(uid ,CODE) VALUES (4,'430101200001014326');
......

完成上述操作後,數據庫tb_idcard表中的數據。

在項目的cn.dsscm.pojo包下創建持久化類IdCard,編輯後的代碼,如示例所示。

 【示例1】  IdCard.java
public class IdCard {
	private Integer id; // id
	private Integer uid; // 用戶id
	private String code;// 身份證號碼

	private User user;// 一對一
	//省略getter和setter方法
}

在上述示例中,分別定義了各自的屬性以及對應的getter/setter方法,同時爲了方便查看輸出結果還重寫了toString()方法。
在cn.dsscm.mapper包中,創建證件映射文件IdCardMapper.xml和用戶映射文件UserMapper.xml,並在兩個映射文件中編寫一對一關聯映射查詢的配置信息,如示例所示。

 【示例2】  IdCardMapper.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="cn.dsscm.dao.IdCardMapper">
  <!-- 嵌套查詢:通過執行另外一條SQL映射語句來返回預期的特殊類型 -->
  <select id="findCodeById" parameterType="Integer" resultMap="IdCardById">
	  SELECT * FROM tb_idcard WHERE id=#{id}
  </select>
  <resultMap type="IdCard" id="IdCardById">
		<id property="id" column="id" />
		<result property="userName" column="userName" />
		<!-- 一對一:association使用select屬性引入另外一條SQL語句 -->
		<association property="user" column="uid" javaType="User"
			select="cn.dsscm.dao.UserMapper.findUserById" />
   </resultMap>
</mapper>
【示例3】  UserMapper.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="cn.dsscm.dao.UserMapper">
     <!-- 根據id查詢用戶信息 -->
	<select id="findUserById" parameterType="Integer" resultType="User">
		SELECT * from tb_user where id=#{id}
	</select>
</mapper>

在上述兩個映射文件中,使用了MyBatis中的嵌套查詢方式進行了個人及其關聯的證件信息查詢,因爲返回的個人對象中除了基本屬性外還有一個關聯的uid屬性,所以需要手動編寫結果映射。從映射文件IdCardMapper.xml中可以看出,嵌套查詢的方法是先執行一個簡單的SQL語句,然後在進行結果映射時,將關聯對象在<association>元素中使用select屬性執行另一條SQL語句(即 IdCardMapper.xml中的SQL )。
創建映射文件的對應接口如下:

public List<IdCard> findCodeById(@Param("id")Integer id);

在覈心配置文件mybatis-config.xml中,引入Mapper映射文件並定義別名,如下所示。

 【示例4】  mybatis-config.xml
	<!-- 將mapper文件加入到配置文件中 -->
	<mappers>
		<mapper resource="cn/dsscm/dao/UserMapper.xml" />
		<mapper resource="cn/dsscm/dao/IdCardMapper.xml" />
	</mappers>

在上述核心配置文件中,首先引入了數據庫連接的配置文件,然後使用掃描包的形式自定義別名,接下來進行環境的配置,最後配置了Mapper映射文件的位置信息。
在測試包中,創建測試類UserMapperTest,並在類中編寫測試方法getUserListByIdTest (),如下所示。

 【示例5】  UserMapperTest.java
	@Test
	public void getUserListByIdTest(){
		SqlSession sqlSession = null;
		List<IdCard> userList = new ArrayList<IdCard>();
		Integer id = 3;
		try {
			sqlSession = MyBatisUtils.createSqlSession();
			userList = sqlSession.getMapper(IdCardMapper.class).findCodeById(id);
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}finally{
			MyBatisUtils.closeSqlSession(sqlSession);
		}
		
		logger.debug("getUserListByRoleIdTest userList.size : " + userList.size());
		for(IdCard user:userList){
			logger.debug(user);
		}
	}

在getUserListByIdTest()方法中,首先通過MybatisUtils工具類獲取了SqlSession對象,然後通過SqlSession對象的接口方法獲取了用戶信息。爲了查看結果,這裏使用了輸出語句輸出查詢結果信息。最後程序執行完畢時,關閉了SqlSession。
使用JUnit4執行getUserListByIdTest()方法後,控制檯的輸出結果如下:

cn.dsscm.dao.IdCardMapper.findCodeById - ==>  Preparing: SELECT * FROM tb_idcard WHERE id=? 
cn.dsscm.dao.IdCardMapper.findCodeById - ==> Parameters: 3(Integer)
......
cn.dsscm.dao.UserMapper.findUserById - ==>  Preparing: SELECT * from tb_user where id=? 
cn.dsscm.dao.UserMapper.findUserById - ==> Parameters: 3(Integer)
......
cn.dsscm.test.UserMapperTest - getUserListByRoleIdTest userList.size : 1
cn.dsscm.test.UserMapperTest - IdCard [id=3, uid=null, code=430101200001011235, user=User [id=3, userCode=zhangwei, userName=張偉, userPassword=0000000, birthday=Sun Jun 05 00:00:00 CST 1994, gender=2, phone=18567542321, email=null, address=北京市朝陽區, userDesc=null, userRole=2, createdBy=1, imgPath=null, creationDate=Thu Oct 24 13:01:51 CST 2019, modifyBy=null, modifyDate=null, age=null, userRoleName=null]]

從控制檯的輸出結果可以看出,使用MyBatis嵌套查詢的方式查詢出了用戶身份證信息及其用戶的信息,這就是MyBatis中的一對一關聯查詢。
修改代碼,使用MyBatis嵌套結果的方式查詢出了用戶身份證信息及其用戶的信息。
創建映射文件的對應接口如下:

  public List<IdCard> findCodeById2(@Param("uid")Integer id);

在cn.dsscm.mapper包中,修改證件映射文件IdCardMapper.xml,並在映射文件中使用MyBatis嵌套結果編寫一對一關聯映射查詢的配置信息,如示例所示。

 【示例6】  IdCardMapper.xml
	<!-- 根據roleId獲取用戶列表 association start-->
	<resultMap type="IdCard" id="userRoleResult2">
		<id property="id" column="id"/>
		<result property="code" column="code"/>
		<association property="user" javaType="User">
			<id property="id" column="cid"/>
			<result property="userName" column="userName" />
		</association>
	</resultMap>
	<select id="findCodeById2" parameterType="Integer" resultMap="userRoleResult2">
		SELECT u.* ,c.id cid ,c.code
		  FROM tb_user u, tb_idcard c
		 WHERE u.id=c.uid
		       AND c.uid= #{uid}
	</select>

在測試包中,在類中編寫測試方法getUserListByIdTest2(),如下所示。

 【示例7】  UserMapperTest.java
	@Test
	public void getUserListByIdTest2(){
		SqlSession sqlSession = null;
		List<IdCard> userList = new ArrayList<IdCard>();
		Integer id = 3;
		try {
			sqlSession = MyBatisUtils.createSqlSession();
			userList = sqlSession.getMapper(IdCardMapper.class).findCodeById2(id);
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}finally{
			MyBatisUtils.closeSqlSession(sqlSession);
		}
		logger.debug("getUserListByRoleIdTest userList.size : " + userList.size());
		for(IdCard user:userList){
			logger.debug(user);
		}
	}

使用JUnit4執行getUserListByIdTest2()方法後,控制檯的輸出結果如下:

cn.dsscm.dao.IdCardMapper.findCodeById2 - ==>  Preparing: SELECT u.* ,c.id cid ,c.code FROM tb_user u, tb_idcard c WHERE u.id=c.uid AND c.uid= ? 
cn.dsscm.dao.IdCardMapper.findCodeById2 - ==> Parameters: 3(Integer)
......
cn.dsscm.test.UserMapperTest - getUserListByRoleIdTest userList.size : 1
cn.dsscm.test.UserMapperTest - IdCard [id=3, uid=null, code=430101200001011235, user=User [id=3, userCode=null, userName=張偉, userPassword=null, birthday=null, gender=null, phone=null, email=null, address=null, userDesc=null, userRole=null, createdBy=null, imgPath=null, creationDate=null, modifyBy=null, modifyDate=null, age=null, userRoleName=null]]

上述示例使用身份證類關聯用戶信息,改變實體類用用戶類關聯身份證類也可以實現同樣效果,此處不再贅述。
雖然使用嵌套查詢的方式比較簡單,但是從控制檯的輸出結果中可以看出,MyBatis嵌套查詢的方式要執行多條SQL語句,這對於大型數據集合和列表展示不是很好,因爲這樣可能會導致成百上千條關聯的SQL語句被執行,從而極大地消耗數據庫性能並且會降低查詢效率。這並不是開發人員所期望的。爲此,我們可以使用MyBatis提供的嵌套結果方式,來進行關聯查詢。

示例:用戶和用戶角色關聯

association:映射到JavaBean的某個"複雜類型"屬性,比如JavaBean類,即JavaBean內部嵌套一個複雜數據類型(JavaBean)屬性,這種情況就屬於複雜類型的關聯。但是需要注意:association僅處理一對一的關聯關係。

在實際的開發項目中此類絕對的雙向的一對一的關聯比較少見,很多時候是單向的。比如用戶角色和用戶列表關係,從不同角度看映射關係不一樣,這裏面涉及用戶表(tb_user)和用戶權限表(tb_role),從用戶角度關聯權限信息這是一對一,從用戶權限關聯用戶信息是一對多。如果根據用戶角色id獲取該角色下的用戶列表的情況,我們只需要根據用戶表關聯用戶角色表,association便可以處理此種情況下一對一的關聯關係,那麼對於用戶角色關聯用戶信息的一對多的關聯關係的處理,則需要collection元素來實現了,這個後面介紹。

創建數據表,在dsscm數據庫中重新創建名爲tb_idcard的數據表,同時預先插入兩條數據。其執行的SQL語句如下所示。

#用戶表
CREATE TABLE `tb_user` (
   `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵ID',
   `userCode` varchar(15) NOT NULL COMMENT '用戶編碼',
   `userName` varchar(15) NOT NULL COMMENT '用戶名稱',
   `userPassword` varchar(15) NOT NULL COMMENT '用戶密碼',
   `gender` int(10) DEFAULT NULL COMMENT '性別(1:女、 2:男)',
   `birthday` date DEFAULT NULL COMMENT '出生日期',
   `email` varchar(50) DEFAULT NULL COMMENT '郵箱',
   `phone` varchar(15) COMMENT '手機',
   `address` varchar(30) COMMENT '地址',
   `userDesc` text COMMENT '簡介',
   `userRole` int(10) DEFAULT NULL COMMENT '用戶角色(取自角色表-角色id)',
   `imgPath` varchar(100) DEFAULT NULL COMMENT '用戶照片',
   `createdBy` bigint(20) DEFAULT NULL COMMENT '創建者(userId)',
   `creationDate` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '創建時間',
   `modifyBy` bigint(20) DEFAULT NULL COMMENT '更新者(userId)',
   `modifyDate` datetime DEFAULT NULL COMMENT '更新時間',
   PRIMARY KEY (`id`),
   UNIQUE KEY `userCode` (`userCode`)
 );
 
#用戶權限表
CREATE TABLE `tb_role` (
   `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵ID',
   `roleCode` varchar(50) NOT NULL COMMENT '角色編碼',
   `roleName` varchar(50) NOT NULL COMMENT '角色名稱',
   `createdBy` bigint(20) DEFAULT NULL COMMENT '創建者',
   `creationDate` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '創建時間',
   `modifyBy` bigint(20) DEFAULT NULL COMMENT '修改者',
   `modifyDate` datetime DEFAULT NULL COMMENT '修改時間',
   PRIMARY KEY (`id`),
   UNIQUE KEY `roleCode` (`roleCode`)
 );

首先創建Role類,並增加相應的getter和setter方法,示例代碼如下:

 【示例8】  Role.java
public class Role {
	private Integer id; // id
	private String roleCode; // 角色編碼
	private String roleName; // 角色名稱
	private Integer createdBy; // 創建者
	private Date creationDate; // 創建時間
	private Integer modifyBy; // 更新者
	private Date modifyDate;// 更新時間
	//省略getter和setter方法
}

修改User類,增加角色屬性(Role role),並增加相應的getter和setter方法;註釋掉用戶角色名稱屬性(String userRoleName),並註釋掉其getter和setter方法,示例代碼如下:

 【示例9】  User.java
public class User {
	private Integer id; //id 
	private String userCode; //用戶編碼
	private String userName; //用戶名稱
	private String userPassword; //用戶密碼
	private Integer gender;  //性別
	private Date birthday;  //出生日期
	private String phone;   //電話
	private String address; //地址
	private Integer userRole;    //用戶角色ID
	private Integer createdBy;   //創建者
	private Date creationDate; //創建時間
	private Integer modifyBy;     //更新者
	private Date modifyDate;   //更新時間
	
	private Integer age;//年齡
	//private String userRoleName; //用戶角色名稱
	
	//association
	private Role role; //用戶角色
	
	//省略getter和setter方法
}

通過以上改造,我們的JavaBean:User對象內部嵌套了一個複雜數據類型的屬性:role。接下來在UserMapper接口裏增加根據角色id獲取用戶列表的方法,代碼如下:

public List<User> getUserListByRoleId(@Param("userRole")Integer roleId);

修改對應UserMapper.xml,增加getUserListByRoleId,該select查詢語句返回類型爲resultMap,並且外部引用的resultMap的類型爲User。由於User對象內嵌JavaBean對象(Role),因此需要使用association來實現結果映射。代碼如下:

 【示例10】  UserMapper.xml
	<!-- 根據roleId獲取用戶列表 association start-->
	<resultMap type="User" id="userRoleResult">
		<id property="id" column="id"/>
		<result property="userCode" column="userCode" />
		<result property="userName" column="userName" />
		<result property="userRole" column="userRole" />
		<association property="role" javaType="Role">
			<id property="id" column="r_id"/>
			<result property="roleCode" column="roleCode"/>
			<result property="roleName" column="roleName"/>
		</association>
	</resultMap>
	<select id="getUserListByRoleId" parameterType="Integer" resultMap="userRoleResult">
		select u.*,r.id as r_id,r.roleCode,r.roleName
		  from tb_user u,tb_role r 
		 where u.userRole = #{userRole} and u.userRole = r.id
	</select>

從上述代碼,簡單分析association的屬性。

  • javaType:完整Java類名或者別名。若映射到一個JavaBean,則MyBatis通常會自行檢測到其類型;若映射到一個HashMap,則應該明確指定javaType,來確保所需行爲。此處爲Role。
  • property :映射數據庫列的實體對象的屬性。此處爲在User裏定義的屬性:role。
  • association的子元素如下: id; result;property:映射數據庫列的實體對象的屬性。此處爲Role的屬性; column:數據庫列名或別名。

在做結果映射的過程中,需要注意:要確保所有的列名都是唯一且無歧義的。id子元素在嵌套結果映射中扮演了非常重要的角色,應該指定一個或者多個屬性來唯一標識這個結果集。實際上,即便沒有指定id,MyBatis也會工作,但是會導致嚴重的性能開銷,所以最好選擇儘量少的屬性來唯一標識結果,主鍵或者聯合主鍵均可。

最後修改測試類UserMapperTest.java,增加測試方法,示例代碼如下:

 【示例11】  UserMapperTest.java
	@Test
	public void getUserListByRoleIdTest(){
		SqlSession sqlSession = null;
		List<User> userList = new ArrayList<User>();
		Integer roleId = 3;
		try {
			sqlSession = MyBatisUtils.createSqlSession();
			userList = sqlSession.getMapper(UserMapper.class).getUserListByRoleId(roleId);
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}finally{
			MyBatisUtils.closeSqlSession(sqlSession);
		}
		
		logger.debug("getUserListByRoleIdTest userList.size : " + userList.size());
		for(User user:userList){
			logger.debug("userList =====> userName: " + user.getUserName() 
				+", <未做映射字段>userPassworD.	" + user.getUserPassword()
				+ ", Role: " + user.getRole().getId() + " --- " 
				+ user.getRole().getRoleCode() +" --- " + user.getRole().getRoleName());
		}
	}

在測試方法中調用getUserListByRoleId()方法獲取userList,並進行結果輸出,關鍵是映射的用戶角色相關信息。

cn.dsscm.dao.UserMapper.getUserListByRoleId - ==>  Preparing: select u.*,r.id as r_id, r.roleCode ,r.roleName from tb_user u,tb_role r where u.userRole = ? and u.userRole = r.id 
cn.dsscm.dao.UserMapper.getUserListByRoleId - ==> Parameters: 3(Integer)
......
cn.dsscm.test.UserMapperTest - getUserListByRoleIdTest userList.size : 7
cn.dsscm.test.UserMapperTest - userList =====> userName: 張華, <未做映射字段>userPassworD.	null, Role: 3 --- DSSCM_EMPLOYEE --- 普通員工
cn.dsscm.test.UserMapperTest - userList =====> userName: 王洋, <未做映射字段>userPassworD.	null, Role: 3 --- DSSCM_EMPLOYEE --- 普通員工
cn.dsscm.test.UserMapperTest - userList =====> userName: 趙燕, <未做映射字段>userPassworD.	null, Role: 3 --- DSSCM_EMPLOYEE --- 普通員工
......

通過上面的示例,我們瞭解了關於association的基本用法以及適用場景,那麼現在再思考一個問題:上一個例子中使用"userRoleResult"聯合一個association的結果映射來加載User實例,那麼association的role結果映射是否可複用?
答案是肯定的,association提供了的另一個屬性:resultMap。通過這個屬性可以擴展一個resultMap來進行聯合映射,這樣就可以使role結果映射重複使用。當然,若不需要重用,也可按照之前的寫法,直接嵌套這個聯合結果映射,根據具體業務而定。下面就來改造剛纔的示例,使用resultMap完成association的role映射結果的複用。具體操作如下。
修改UserMapper.xml,增加resultMap來完成role的結果映射,association增加屬性resultMap來引用外部的"roleResult",代碼如下:

【示例12】  UserMapper.xml
	<!-- 根據roleId獲取用戶列表 association start-->
	<resultMap type="User" id="userRoleResult2">
		<id property="id" column="id"/>
		<result property="userCode" column="userCode" />
		<result property="userName" column="userName" />
		<result property="userRole" column="userRole" />
		<association property="role" javaType="Role" resultMap="roleResult"/>
	</resultMap>
	<resultMap type="Role" id="roleResult">
		<id property="id" column="r_id"/>
		<result property="roleCode" column="roleCode"/>
		<result property="roleName" column="roleName"/>
	</resultMap>
	<select id="getUserListByRoleId2" parameterType="Integer" resultMap="userRoleResult2">
		select u.*,r.id as r_id,r.roleCode,r.roleName from tb_user u,tb_role r 
		 where u.userRole = #{userRole} and u.userRole = r.id
	</select>

在上述代碼中,把之前的角色結果映射代碼抽取出來放在一個resultMap中,然後設置了association的resultMap屬性來引用外部的"roleResult"。這樣做的好處就是可以達到複用的效果,並且整體的結構較爲清晰明瞭,特別適合association的結果映射比較多的情況。
運行結果如下:

cn.dsscm.dao.UserMapper.getUserListByRoleId3 - ==>  Preparing: select * from tb_user u where u.userRole = ? 
cn.dsscm.dao.UserMapper.getUserListByRoleId3 - ==> Parameters: 3(Integer)
......
cn.dsscm.dao.UserMapper.getRoleList - ==>  Preparing: select * from tb_role where id=? 
cn.dsscm.dao.UserMapper.getRoleList - ==> Parameters: 3(Integer)
......
cn.dsscm.test.UserMapperTest - getUserListByRoleIdTest userList.size : 7
cn.dsscm.test.UserMapperTest - userList =====> userName: 張華, <未做映射字段>userPassworD.	0000000, Role: 3 --- DSSCM_EMPLOYEE --- 普通員工
cn.dsscm.test.UserMapperTest - userList =====> userName: 王洋, <未做映射字段>userPassworD.	0000000, Role: 3 --- DSSCM_EMPLOYEE --- 普通員工
cn.dsscm.test.UserMapperTest - userList =====> userName: 趙燕, <未做映射字段>userPassworD.	0000000, Role: 3 --- DSSCM_EMPLOYEE --- 普通員工
......

從控制檯的輸出結果可以看出,使用MyBatis嵌套查詢的方式查詢出了用戶及其權限的信息,這就是MyBatis中的一對一關聯查詢。
雖然使用嵌套查詢的方式比較簡單,但是從控制檯的輸出結果中可以看出,MyBatis嵌套查詢的方式要執行多條SQL語句,這對於大型數據集合和列表展示不是很好,因爲這樣可能會導致成百上千條關聯的SQL語句被執行,從而極大地消耗數據庫性能並且會降低查詢效率。這並不是開發人員所期望的。爲此,我們可以使用MyBatis提供的嵌套結果方式,來進行關聯查詢。
在PersonMapper.xml中,使用MyBatis嵌套結果的方式進行個人及其關聯的證件信息查詢,所添加的代碼如下所示。

 【示例13】  UserMapper.xml
	<!-- 嵌套結果:使用嵌套結果映射來處理重複的聯合結果的子集 -->
	<select id="findPersonById2" parameterType="Integer" resultMap="IdCardWithPersonResult2">
	    SELECT p.*,idcard.code
	    from tb_person p,tb_idcard idcard
	    where p.card_id=idcard.id 
	    and p.id= #{id}
	</select>
	<resultMap type="Person" id="IdCardWithPersonResult2">
	    <id property="id" column="id" />
	    <result property="name" column="name" />
	    <result property="age" column="age" />
	    <result property="sex" column="sex" />
	    <association property="card" javaType="IdCard">
	        <id property="id" column="card_id" />
	        <result property="code" column="code" />
	    </association>
	</resultMap>

從上述代碼中可以看出,MyBatis嵌套結果的方式只編寫了一條複雜的多表關聯的SQL語句,並且在<association>元素中繼續使用相關子元素進行數據庫表字段和實體類屬性的一一映射。
在測試類中編寫測試方法getUserListByRoleIdTest3(),其代碼如下所示。

【示例14】  UserMapperTest.java
	@Test
	public void getUserListByRoleIdTest3(){
		SqlSession sqlSession = null;
		List<User> userList = new ArrayList<User>();
		Integer roleId = 3;
		try {
			sqlSession = MyBatisUtils.createSqlSession();
			userList = sqlSession.getMapper(UserMapper.class).getUserListByRoleId3(roleId);
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}finally{
			MyBatisUtils.closeSqlSession(sqlSession);
		}
		logger.debug("getUserListByRoleIdTest userList.size : " + userList.size());
		for(User user:userList){
			logger.debug("userList =====> userName: " + user.getUserName() 
			+", <未做映射字段>userPassworD.	" + user.getUserPassword()		
			+ ", Role: " + user.getRole().getId() + " --- " 
			+ user.getRole().getRoleCode() +" --- " + user.getRole().getRoleName());
		}
	}

使用JUnit4執行getUserListByRoleIdTest3()方法後,控制檯的輸出結果如下:

cn.dsscm.dao.UserMapper.getUserListByRoleId3 - ==>  Preparing: select * from tb_user u where u.userRole = ? 
cn.dsscm.dao.UserMapper.getUserListByRoleId3 - ==> Parameters: 3(Integer)
......
cn.dsscm.dao.UserMapper.getRoleList - ==>  Preparing: select * from tb_role where id=? 
cn.dsscm.dao.UserMapper.getRoleList - ==> Parameters: 3(Integer)
......
cn.dsscm.test.UserMapperTest - getUserListByRoleIdTest userList.size : 7
cn.dsscm.test.UserMapperTest - userList =====> userName: 張華, <未做映射字段>userPassworD.	0000000, Role: 3 --- DSSCM_EMPLOYEE --- 普通員工
cn.dsscm.test.UserMapperTest - userList =====> userName: 王洋, <未做映射字段>userPassworD.	0000000, Role: 3 --- DSSCM_EMPLOYEE --- 普通員工
cn.dsscm.test.UserMapperTest - userList =====> userName: 趙燕, <未做映射字段>userPassworD.	0000000, Role: 3 --- DSSCM_EMPLOYEE --- 普通員工
......

從控制檯的輸出結果可以看出,使用MyBatis嵌套結果的方式只執行了一條SQL語句,並且同樣查詢出了個人及其關聯的身份證的信息。

經驗

MyBatis延遲加載的配置,在使用MyBatis嵌套查詢方式進行MyBatis關聯查詢映射時,使用MyBatis的延遲加載在一定程度上可以降低運行消耗並提高查詢效率。MyBatis默認沒有開啓延遲加載,需要在覈心配置文件mybatis-config.xml中的<settings>元素內進行配置,具體配置方式如下。

<settings>
     <!--打開延遲加載的開關--> 
     <setting name="lazyLoadingEnabled" value="true" /> 
     <!--將積極加載改爲消息加載,即按需加載-->
     <setting name="aggressiveLazyLoading" value="false"/> 
</settings>

在映射文件中,MyBatis關聯映射的<association>元素和<collection>元素中都已默認配置了延遲加載屬性,即默認屬性fetchType=“lazy”(屬性fetchType="eager"表示立即加栽),所以在配置文件中開啓延遲加載後,無須在映射文件中再做配置。


超全面的測試IT技術課程,0元立即加入學習!有需要的朋友戳:

騰訊課堂測試技術學習地址

歡迎轉載,但未經作者同意請保留此段聲明,並在文章頁面明顯位置給出原文鏈接。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章