最近在讀劉增輝老師所著的《MyBatis從入門到精通》一書,很有收穫,於是將自己學習的過程以部落格形式輸出,如有錯誤,歡迎指正,如幫助到你,不勝榮幸!
1. 多表查詢
上篇部落格中,我們示例的2個查詢都是單表查詢,但實際的業務場景肯定是需要多表查詢的,比如現在有個需求:
查詢某個使用者擁有的所有角色。這個需求要涉及到sys_user,sys_user_role,sys_role三張表,如何實現呢?
首先,在SysUserMapper介面中定義如下方法。
/**
* 根據使用者id獲取角色資訊
*
* @param userId
* @return
*/
List<SysRole> selectRolesByUserId(Long userId);
然後開啟對應的SysUserMapper.xml檔案,新增如下select語句:
<select id="selectRolesByUserId" resultType="com.zwwhnly.mybatisaction.model.SysRole">
SELECT r.id,
r.role_name roleName,
r.enabled,
r.create_by createBy,
r.create_time 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 = #{userId}
</select>
細心的讀者可能會發現,我們雖然使用到了多表查詢,但是resultType設定的仍然是單表,即只包含角色表的資訊。
如果我希望這個查詢語句同時返回SysUser表的user_name欄位呢,該如何設定resultType?
方法1:直接在SysRole實體類中新增userName欄位。
private String userName;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
此時resultType不用修改。
方法2:新建擴充套件類,在擴充套件類中新增userName欄位。
package com.zwwhnly.mybatisaction.model;
public class SysRoleExtend extends SysRole {
private String userName;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
}
此時需要將resultType修改為:com.zwwhnly.mybatisaction.model.SysRoleExtend。
這種方式比較適合需要少量額外欄位的場景。如果需要其他表的大量欄位,可以使用方式3或者方式4,個人推薦使用方式4。
方法3:在SysRole實體類中新增SysUser型別的欄位。
private SysUser sysUser;
public SysUser getSysUser() {
return sysUser;
}
public void setSysUser(SysUser sysUser) {
this.sysUser = sysUser;
}
此時resultType不用修改。
方法4(推薦使用):新建擴充套件類,在擴充套件類中新增SysUser型別的欄位。
書中推薦的是方式3,方式4是我個人認為更好的方式,因為實體類一般由工具自動生成,增加了欄位後,後續容易忘記導致被覆蓋掉。
package com.zwwhnly.mybatisaction.model;
public class SysRoleExtend extends SysRole {
private SysUser sysUser;
public SysUser getSysUser() {
return sysUser;
}
public void setSysUser(SysUser sysUser) {
this.sysUser = sysUser;
}
}
此時需要將resultType修改為:com.zwwhnly.mybatisaction.model.SysRoleExtend。
此時xml中的查詢語句如下。
<select id="selectRolesByUserId" resultType="com.zwwhnly.mybatisaction.model.SysRoleExtend">
SELECT r.id,
r.role_name roleName,
r.enabled,
r.create_by createBy,
r.create_time createTime,
u.user_name "sysUser.userName",
u.user_email "sysUser.userEmail"
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 = #{userId}
</select>
在SysUserMapperTest中新增測試程式碼如下。
@Test
public void testSelectRolesByUserId() {
SqlSession sqlSession = getSqlSession();
try {
SysUserMapper sysUserMapper = sqlSession.getMapper(SysUserMapper.class);
List<SysRole> sysRoleList = sysUserMapper.selectRolesByUserId(1L);
Assert.assertNotNull(sysRoleList);
Assert.assertTrue(sysRoleList.size() > 0);
} finally {
sqlSession.close();
}
}
執行該測試方法,輸入日誌如下。
DEBUG [main] - ==> Preparing: SELECT r.id, r.role_name roleName, r.enabled, r.create_by createBy, r.create_time createTime, u.user_name "sysUser.userName", u.user_email "sysUser.userEmail" 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 = ?
DEBUG [main] - ==> Parameters: 1(Long)
TRACE [main] - <== Columns: id, roleName, enabled, createBy, createTime, sysUser.userName, sysUser.userEmail
TRACE [main] - <== Row: 1, 管理員, 1, 1, 2019-06-27 18:21:12.0, admin, admin@mybatis.tk
TRACE [main] - <== Row: 2, 普通使用者, 1, 1, 2019-06-27 18:21:12.0, admin, admin@mybatis.tk
DEBUG [main] - <== Total: 2
2. 多個介面引數的用法
2.1 引數型別是基本型別
截止目前,我們定義的方法都只有1個引數,要麼是隻有1個基本型別的引數,比如selectById(Long id);。
要麼是隻有1個物件作為引數,即將多個引數合併成了1個物件。
但有些場景下,比如只有2個引數,沒有必要為這2個引數再新建一個物件,比如我們現在需要根據使用者的id和角色的狀態來獲取使用者的所有角色,那麼該如何使用呢?
首先,在介面SysUserMapper中新增如下方法。
/**
* 根據使用者id和角色的enabled狀態獲取使用者的角色
*
* @param userId
* @param enabled
* @return
*/
List<SysRole> selectRolesByUserIdAndRoleEnabled(Long userId,Integer enabled);
然後,開啟對應的SysUserMapper.xml檔案,新增如下程式碼。
<select id="selectRolesByUserIdAndRoleEnabled" resultType="com.zwwhnly.mybatisaction.model.SysRole">
SELECT r.id,
r.role_name roleName,
r.enabled,
r.create_by createBy,
r.create_time 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 = #{userId} AND r.enabled = #{enabled}
</select>
在SysUserMapperTest測試類中,新增如下測試方法。
@Test
public void testselectRolesByUserIdAndRoleEnabled() {
SqlSession sqlSession = getSqlSession();
try {
SysUserMapper sysUserMapper = sqlSession.getMapper(SysUserMapper.class);
List<SysRole> sysRoleList = sysUserMapper.selectRolesByUserIdAndRoleEnabled(1L, 1);
Assert.assertNotNull(sysRoleList);
Assert.assertTrue(sysRoleList.size() > 0);
} finally {
sqlSession.rollback();
sqlSession.close();
}
}
執行該測試方法,發現報如下錯誤。
報錯資訊中說未找到引數userId,可用的引數是[0,1,param1,param2],也就是說我們將程式碼修改為:
WHERE u.id = #{0} AND r.enabled = #{1}
或者修改為:
WHERE u.id = #{param1} AND r.enabled = #{param2}
這麼使用是可以測試通過的,不過這樣使用,程式碼閱讀起來不夠友好,因此並不推薦這麼使用。
推薦在介面方法的引數前新增@Param註解,如下所示:
/**
* 根據使用者id和角色的enabled狀態獲取使用者的角色
*
* @param userId
* @param enabled
* @return
*/
List<SysRole> selectRolesByUserIdAndRoleEnabled(@Param("userId") Long userId, @Param("enabled") Integer enabled);
執行剛剛新增的測試方法,測試通過,輸出日誌如下:
DEBUG [main] - ==> Preparing: SELECT r.id, r.role_name roleName, r.enabled, r.create_by createBy, r.create_time 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 = ? AND r.enabled = ?
DEBUG [main] - ==> Parameters: 1(Long), 1(Integer)
TRACE [main] - <== Columns: id, roleName, enabled, createBy, createTime
TRACE [main] - <== Row: 1, 管理員, 1, 1, 2019-06-27 18:21:12.0
TRACE [main] - <== Row: 2, 普通使用者, 1, 1, 2019-06-27 18:21:12.0
DEBUG [main] - <== Total: 2
2.2 引數型別是物件
為了演示引數型別是物件的使用方法,我們在介面SysUserMapper中新增如下方法:
/**
* 根據使用者id和角色的enabled狀態獲取使用者的角色
*
* @param user
* @param role
* @return
*/
List<SysRole> selectRolesByUserAndRole(@Param("user") SysUser user, @Param("role") SysRole role);
此時對應的xml中的語句為:
<select id="selectRolesByUserAndRole" resultType="com.zwwhnly.mybatisaction.model.SysRole">
SELECT r.id,
r.role_name roleName,
r.enabled,
r.create_by createBy,
r.create_time 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 = #{user.id} AND r.enabled = #{role.enabled}
</select>
3. 原始碼
原始碼地址:https://github.com/zwwhnly/mybatis-action.git,歡迎下載。
4. 參考
劉增輝《MyBatis從入門到精通》