MyBatis-20MyBatis高级结果映射【一对一映射(4种方式)】
文章目錄
- 概述
- 需求
- 方式一:使用自動映射處理一對一映射
- 實體類改造
- UserMapper接口增加接口方法
- UserMapper.xml增加SQL
- 單元測試
- 方式二:使用resultMap配置一對一映射
- UserMapper接口增加接口方法
- UserMapper.xml增加SQL
- 單元測試
- 方式三:使用resultMap的asscociation標簽配置一對一映射
- UserMapper接口增加接口方法
- UserMapper.xml增加SQL
- 單元測試
- 方式四:asscociation標簽的嵌套查詢
概述
我們還是繼續使用我們的RBAC權限系統來演示。
假設在RBAC權限系統中,一個用戶只能擁有一個角色。
需求
實現在查詢用戶信息的同時獲取用戶擁有的角色信息
方式一:使用自動映射處理一對一映射
實體類改造
一個用戶擁有一個角色,因此現在SysUser類中增加SysRole字段
/*** * * @ClassName: SysUser* * @Description: 用戶表* * @author: Mr.Yang* * @date: 2018年4月13日 下午9:24:21*/ public class SysUser {//其他原有字段以及setter getter/*** 用戶角色*/private SysRole sysRole;// setter getterpublic SysRole getSysRole() {return sysRole;}public void setSysRole(SysRole sysRole) {this.sysRole = sysRole;}}使用自動映射就是通過別名讓MyBatis自動將值匹配到對應的子彈上,簡單的別名映射如user_name 對應userName .
除此之外MyBatis還支持復雜的屬性映射,可以多層嵌套。 比如將role.role_name 映射到 role.roleName上。 MyBatis首先會查找role屬性,如果存在role屬性就創建role對象,然后在role對象中繼續查找roleName, 將role_name的值綁定到role對象的roleName屬性上 。
UserMapper接口增加接口方法
/*** * * @Title: selectSysUserAndSysRoleById* * @Description: 根據Id查詢用戶信息的同時獲取用戶擁有的角色信息* * @param id* @return* * @return: SysUser*/SysUser selectSysUserAndSysRoleById(Long id);UserMapper.xml增加SQL
<select id="selectSysUserAndSysRoleById" resultType="com.artisan.mybatis.xml.domain.SysUser">SELECTu.id,u.user_name userName,u.user_password userPassword,u.user_email userEmail,u.user_info userInfo,u.create_time createTime,u.head_img headImg,r.id "sysRole.id",r.role_name "sysRole.roleName",r.enabled "sysRole.enabled",r.create_by "sysRole.createBy",r.create_time "sysRole.createTime"FROMsys_user uINNER JOIN sys_user_role ur ON u.id = ur.user_idINNER JOIN sys_role r ON ur.role_id = r.idWHEREu.id = #{id}</select> 注意上述SQL中 sys_role查詢的列的別名都是 “sysRole.”前綴,這和SysUser實體類中SysRole屬性的名稱保持一致,通過這種方式將sysRole的屬性都映射到了SysUser的sysRole屬性上
。
單元測試
@Testpublic void selectSysUserAndSysRoleByIdTest() {logger.info("selectSysUserAndSysRoleByIdTest");// 獲取SqlSessionSqlSession sqlSession = getSqlSession();try {// 獲取UserMapper接口UserMapper userMapper = sqlSession.getMapper(UserMapper.class);// 注意:數據庫中id=1的用戶擁有2個角色,不適合這個例子。// 調用selectSysUserAndSysRoleById方法,查詢id=1001的用戶及其角色SysUser sysUser = userMapper.selectSysUserAndSysRoleById(1001L);// 期望用戶不為空Assert.assertNotNull(sysUser);// 期望角色不為空Assert.assertNotNull(sysUser.getSysRole());logger.info(sysUser);} catch (Exception e) {e.printStackTrace();} finally {sqlSession.close();logger.info("sqlSession close successfully ");}}debug調測下數據
日志
2018-04-29 23:43:15,905 INFO [main] (BaseMapperTest.java:26) - sessionFactory bulit successfully 2018-04-29 23:43:15,913 INFO [main] (BaseMapperTest.java:29) - reader close successfully 2018-04-29 23:43:15,918 INFO [main] (UserMapperTest.java:1007) - selectSysUserAndSysRoleByIdTest 2018-04-29 23:43:16,008 DEBUG [main] (BaseJdbcLogger.java:145) - ==> Preparing: SELECT u.id, u.user_name userName, u.user_password userPassword, u.user_email userEmail, u.user_info userInfo, u.create_time createTime, u.head_img headImg, r.id "sysRole.id", r.role_name "sysRole.roleName", r.enabled "sysRole.enabled", r.create_by "sysRole.createBy", r.create_time "sysRole.createTime" FROM sys_user u INNER JOIN sys_user_role ur ON u.id = ur.user_id INNER JOIN sys_role r ON ur.role_id = r.id WHERE u.id = ? 2018-04-29 23:43:16,135 DEBUG [main] (BaseJdbcLogger.java:145) - ==> Parameters: 1001(Long) 2018-04-29 23:43:16,196 TRACE [main] (BaseJdbcLogger.java:151) - <== Columns: id, userName, userPassword, userEmail, userInfo, createTime, headImg, sysRole.id, sysRole.roleName, sysRole.enabled, sysRole.createBy, sysRole.createTime 2018-04-29 23:43:16,198 TRACE [main] (BaseJdbcLogger.java:151) - <== Row: 1001, artisan, 123456, test@artisan.com, <<BLOB>>, 2018-04-13 21:12:47.0, <<BLOB>>, 2, 普通用戶, 1, 1, 2018-04-13 21:12:46.0 2018-04-29 23:43:16,215 DEBUG [main] (BaseJdbcLogger.java:145) - <== Total: 1 2018-04-29 23:43:16,216 INFO [main] (UserMapperTest.java:1021) - SysUser [id=1001, userName=artisan, userPassword=123456, userEmail=test@artisan.com, userInfo=測試用戶, headImg=[18, 49, 35, 18, 48], createTime=Fri Apr 13 21:12:47 BOT 2018, sysRole=SysRole [id=2, roleName=普通用戶, enabled=1, createBy=1, createTime=Fri Apr 13 21:12:46 BOT 2018, user=null, privilegeList=null]] 2018-04-29 23:43:16,222 INFO [main] (UserMapperTest.java:1026) - sqlSession close successfully通過SQL日志可以看到已經查詢出的一條數據,MyBatis將這條數據映射到了兩個類中,像這種通過一次查詢將結果映射到不同對象的方式,稱之為關聯的嵌套結果查詢。
關聯的嵌套結果映射需要關聯多個表將所有需要的值一次性查詢出來, 這種方式的好處是減少數據庫的查詢次數,減輕數據庫的壓力。 缺點是需要些很復雜的SQL,并且當嵌套結果更負載時,不容易一次寫正確。 由于要在服務器上將結果映射到不同的類上,因此也會增加應用服務器的壓力。 當一定會使用到嵌套查詢,并且整個復雜的SQL執行速度很快時,建議使用關聯的其那套結果查詢。
方式二:使用resultMap配置一對一映射
UserMapper接口增加接口方法
// 使用resultMap配置一對一映射 SysUser selectSysUserAndSysRoleById2(Long id);UserMapper.xml增加SQL
<!-- 使用resultMap配置一對一映射 --><resultMap id="userRoleMap" type="com.artisan.mybatis.xml.domain.SysUser"><id column="id" property="id" /><result property="userName" column="user_name" /><result property="userPassword" column="user_password" /><result property="userEmail" column="user_email" /><result property="userInfo" column="user_info" /><result property="headImg" column="head_img" jdbcType="BLOB" /><result property="createTime" column="create_time" jdbcType="TIMESTAMP" /><!-- sysRole相關的屬性 --><result property="sysRole.id" column="role_id"/><result property="sysRole.roleName" column="role_name"/><result property="sysRole.enabled" column="enabled"/><result property="sysRole.createBy" column="create_by"/><result property="sysRole.createTime" column="role_create_time" jdbcType="TIMESTAMP"/></resultMap>這種配置和上一個配置相似的地方在于,sysRole中的property配置部分使用“sysRole.”前綴,在column部分,為了避免不同表中存在相同的的字段,所有可能重名的列都加了 “role_”前綴。
這種方式配置的時候,還需要再查詢時設置不同的列名,別名和resultMap配置的colunm一致。 然后使用resultMap配置映射。
<!-- 使用resultMap配置一對一映射 --><select id="selectSysUserAndSysRoleById2" resultMap="userRoleMap">SELECTu.id,u.user_name ,u.user_password ,u.user_email ,u.user_info ,u.create_time ,u.head_img ,r.id role_id,r.role_name ,r.enabled ,r.create_by ,r.create_time role_create_time FROMsys_user uINNER JOIN sys_user_role ur ON u.id = ur.user_idINNER JOIN sys_role r ON ur.role_id = r.idWHEREu.id = #{id}</select>單元測試
@Testpublic void selectSysUserAndSysRoleById2Test() {logger.info("selectSysUserAndSysRoleById2Test");// 獲取SqlSessionSqlSession sqlSession = getSqlSession();try {// 獲取UserMapper接口UserMapper userMapper = sqlSession.getMapper(UserMapper.class);// 注意:數據庫中id=1的用戶擁有2個角色,不適合這個例子。// 調用selectSysUserAndSysRoleById方法,查詢id=1001的用戶及其角色SysUser sysUser = userMapper.selectSysUserAndSysRoleById2(1001L);// 期望用戶不為空Assert.assertNotNull(sysUser);// 期望角色不為空Assert.assertNotNull(sysUser.getSysRole());logger.info(sysUser);} catch (Exception e) {e.printStackTrace();} finally {sqlSession.close();logger.info("sqlSession close successfully ");}}日志
2018-04-30 00:52:25,287 INFO [main] (BaseMapperTest.java:26) - sessionFactory bulit successfully 2018-04-30 00:52:25,291 INFO [main] (BaseMapperTest.java:29) - reader close successfully 2018-04-30 00:52:25,295 INFO [main] (UserMapperTest.java:1032) - selectSysUserAndSysRoleById2Test 2018-04-30 00:52:25,390 DEBUG [main] (BaseJdbcLogger.java:145) - ==> Preparing: SELECT u.id, u.user_name , u.user_password , u.user_email , u.user_info , u.create_time , u.head_img , r.id role_id, r.role_name , r.enabled , r.create_by , r.create_time role_create_time FROM sys_user u INNER JOIN sys_user_role ur ON u.id = ur.user_id INNER JOIN sys_role r ON ur.role_id = r.id WHERE u.id = ? 2018-04-30 00:52:25,519 DEBUG [main] (BaseJdbcLogger.java:145) - ==> Parameters: 1001(Long) 2018-04-30 00:52:25,559 TRACE [main] (BaseJdbcLogger.java:151) - <== Columns: id, user_name, user_password, user_email, user_info, create_time, head_img, role_id, role_name, enabled, create_by, role_create_time 2018-04-30 00:52:25,561 TRACE [main] (BaseJdbcLogger.java:151) - <== Row: 1001, artisan, 123456, test@artisan.com, <<BLOB>>, 2018-04-13 21:12:47.0, <<BLOB>>, 2, 普通用戶, 1, 1, 2018-04-13 21:12:46.0 2018-04-30 00:52:25,572 DEBUG [main] (BaseJdbcLogger.java:145) - <== Total: 1 2018-04-30 00:52:25,573 INFO [main] (UserMapperTest.java:1046) - SysUser [id=1001, userName=artisan, userPassword=123456, userEmail=test@artisan.com, userInfo=測試用戶, headImg=[18, 49, 35, 18, 48], createTime=Fri Apr 13 21:12:47 BOT 2018, sysRole=SysRole [id=2, roleName=普通用戶, enabled=1, createBy=1, createTime=Fri Apr 13 21:12:46 BOT 2018, user=null, privilegeList=null]] 2018-04-30 00:52:25,580 INFO [main] (UserMapperTest.java:1051) - sqlSession close successfully這種方法是不是和第一種使用自動映射處理一對一映射相比起來,resultMap相當麻煩?
事實上 ,resultMap映射是可以被繼承的,因此要先簡化上面的resultMap的配置。 因為我們這個映射文件中本來就存在一個userMap的映射配置,改造如下
<!-- 使用resultMap配置一對一映射 繼承原有的resultMap --><resultMap id="userRoleMap_byExtends" extends="userMap"type="com.artisan.mybatis.xml.domain.SysUser"><!-- sysRole相關的屬性 --><result property="sysRole.id" column="role_id"/><result property="sysRole.roleName" column="role_name"/><result property="sysRole.enabled" column="enabled"/><result property="sysRole.createBy" column="create_by"/><result property="sysRole.createTime" column="role_create_time" jdbcType="TIMESTAMP"/></resultMap>使用繼承不僅可以簡化配置,而且當對主表userMap進行修改時也只需要修改一處。 雖然還不是太方便,至少簡潔了一點。 要想更加簡潔,只有派上asscociation了,請往下看
測試過了,就不貼代碼了,github上已經托管。
方式三:使用resultMap的asscociation標簽配置一對一映射
在resultMap中,association標簽用于和一個復雜的類型進行關聯,即用于一對一的關聯配置。
UserMapper接口增加接口方法
// 使用resultMap配置一對一映射 resultMap association SysUser selectSysUserAndSysRoleById4(Long id);UserMapper.xml增加SQL
在上面的基礎上,再做修改,改成association標簽的配置方式。
<!-- 使用resultMap配置一對一映射 使用association --><resultMap id="userRoleMap_byExtendsAndAssociation" extends="userMap"type="com.artisan.mybatis.xml.domain.SysUser"><association property="sysRole" columnPrefix="sysRole_"javaType="com.artisan.mybatis.xml.domain.SysRole"><result property="id" column="id"/><result property="roleName" column="role_name"/><result property="enabled" column="enabled"/><result property="createBy" column="create_by"/><result property="createTime" column="create_time" jdbcType="TIMESTAMP"/></association> </resultMap><!-- 使用resultMap配置一對一映射 result中Map中使用association --><select id="selectSysUserAndSysRoleById4" resultMap="userRoleMap_byExtendsAndAssociation">SELECTu.id,u.user_name ,u.user_password ,u.user_email ,u.user_info ,u.create_time ,u.head_img ,r.id sysRole_id,r.role_name sysRole_role_name,r.enabled sysRole_enabled,r.create_by sysRole_create_by,r.create_time sysRole_create_timeFROMsys_user uINNER JOIN sys_user_role ur ON u.id = ur.user_idINNER JOIN sys_role r ON ur.role_id = r.idWHEREu.id = #{id}</select>association標簽包含如下主要屬性
- property:對應實體列中的屬性名,必填
- javaType:屬性對應的Java類型
- resultMap:可以直接使用現有的resultMap,從而不需要在這里配置
- colunmPrefix: 查詢列的前綴,配置前綴后,在子標簽配置result的colunm時,可以省略前綴
- 其他屬性,略…
因為配置了列的前綴,因此還需要SQL,如上所示。
使用association配置還可以使用resultMap屬性配置成一個已經存在的resultMap映射。 我們吧sys_role相關的映射提取出來,改造如下
<!-- 使用resultMap配置一對一映射 使用association --><resultMap id="roleMap" type="com.artisan.mybatis.xml.domain.SysRole" ><id property="id" column="id"/><result property="roleName" column="role_name"/><result property="enabled" column="enabled"/><result property="createBy" column="create_by"/><result property="createTime" column="create_time" jdbcType="TIMESTAMP"/></resultMap><resultMap id="userRoleMap_byExtendsAndAssociation_ResultMap" extends="userMap"type="com.artisan.mybatis.xml.domain.SysUser"><association property="sysRole" columnPrefix="sysRole_"javaType="com.artisan.mybatis.xml.domain.SysRole" resultMap="roleMap"></association> </resultMap>到這里,是不是沒有這么麻煩了? 還有一個需要注意的地方:roleMap我們目前是寫在UserMapper.xml中,更合理的應該在RoleMapper.xml中。如果真的在RoleMapper.xml中的話,,通過resultMap來引用的話,就必須要加上命名空間了。 如果不加的話,MyBatis會默認添加調用者當前命名空間的前綴。
<resultMap id="userRoleMap_byExtendsAndAssociation_ResultMap" extends="userMap"type="com.artisan.mybatis.xml.domain.SysUser"><association property="sysRole" columnPrefix="sysRole_"javaType="com.artisan.mybatis.xml.domain.SysRole" resultMap="com.artisan.mybatis.xml.mapper.RoleMapper.roleMap"></association> </resultMap>單元測試
@Testpublic void selectSysUserAndSysRoleById4Test() {logger.info("selectSysUserAndSysRoleById4Test");// 獲取SqlSessionSqlSession sqlSession = getSqlSession();try {// 獲取UserMapper接口UserMapper userMapper = sqlSession.getMapper(UserMapper.class);// 注意:數據庫中id=1的用戶擁有2個角色,不適合這個例子。// 調用selectSysUserAndSysRoleById4方法,查詢id=1001的用戶及其角色SysUser sysUser = userMapper.selectSysUserAndSysRoleById4(1001L);// 期望用戶不為空Assert.assertNotNull(sysUser);// 期望角色不為空Assert.assertNotNull(sysUser.getSysRole());logger.info(sysUser);} catch (Exception e) {e.printStackTrace();} finally {sqlSession.close();logger.info("sqlSession close successfully ");}}日志
2018-05-01 01:08:18,898 INFO [main] (BaseMapperTest.java:26) - sessionFactory bulit successfully 2018-05-01 01:08:18,905 INFO [main] (BaseMapperTest.java:29) - reader close successfully 2018-05-01 01:08:18,913 INFO [main] (UserMapperTest.java:1083) - selectSysUserAndSysRoleById4Test 2018-05-01 01:08:19,019 DEBUG [main] (BaseJdbcLogger.java:145) - ==> Preparing: SELECT u.id, u.user_name , u.user_password , u.user_email , u.user_info , u.create_time , u.head_img , r.id sysRole_id, r.role_name sysRole_role_name, r.enabled sysRole_enabled, r.create_by sysRole_create_by, r.create_time sysRole_create_time FROM sys_user u INNER JOIN sys_user_role ur ON u.id = ur.user_id INNER JOIN sys_role r ON ur.role_id = r.id WHERE u.id = ? 2018-05-01 01:08:19,155 DEBUG [main] (BaseJdbcLogger.java:145) - ==> Parameters: 1001(Long) 2018-05-01 01:08:19,212 TRACE [main] (BaseJdbcLogger.java:151) - <== Columns: id, user_name, user_password, user_email, user_info, create_time, head_img, sysRole_id, sysRole_role_name, sysRole_enabled, sysRole_create_by, sysRole_create_time 2018-05-01 01:08:19,213 TRACE [main] (BaseJdbcLogger.java:151) - <== Row: 1001, artisan, 123456, test@artisan.com, <<BLOB>>, 2018-04-13 21:12:47.0, <<BLOB>>, 2, 普通用戶, 1, 1, 2018-04-13 21:12:46.0 2018-05-01 01:08:19,231 DEBUG [main] (BaseJdbcLogger.java:145) - <== Total: 1 2018-05-01 01:08:19,232 INFO [main] (UserMapperTest.java:1097) - SysUser [id=1001, userName=artisan, userPassword=123456, userEmail=test@artisan.com, userInfo=測試用戶, headImg=[18, 49, 35, 18, 48], createTime=Fri Apr 13 21:12:47 BOT 2018, sysRole=SysRole [id=2, roleName=普通用戶, enabled=1, createBy=1, createTime=Fri Apr 13 21:12:46 BOT 2018, user=null, privilegeList=null]] 2018-05-01 01:08:19,239 INFO [main] (UserMapperTest.java:1102) - sqlSession close successfully方式四:asscociation標簽的嵌套查詢
前面三種方式通過負載的SQL查詢獲取結果,其實還可以利用簡單的SQL通過多次查詢轉換為我們需要的結果,這種方式與根據業務邏輯手動執行多次SQL的方式很像,最后將結果組成一個對象。
場景和情況比較復雜,后續單獨闡述
總結
以上是生活随笔為你收集整理的MyBatis-20MyBatis高级结果映射【一对一映射(4种方式)】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MyBatis-19MyBatis代码生
- 下一篇: MyBatis-21MyBatis高级结