MyBatis多表聯(lián)查返回List僅一條數(shù)據(jù)?主鍵沖突BUG排查與解決方案
一、問題背景
在項目開發(fā)中,多表聯(lián)查是高頻需求。近期在基于MyBatis實現(xiàn)"管理員-角色-權限"多表關聯(lián)查詢時,遇到了一個詭異的BUG:數(shù)據(jù)庫執(zhí)行SQL能返回多條正確結果,但項目中通過MyBatis映射后,List集合始終只顯示一條數(shù)據(jù)。
經(jīng)過反復排查,最終定位到是主鍵字段命名沖突導致MyBatis數(shù)據(jù)映射異常,現(xiàn)將完整排查過程與解決方案分享如下。
二、問題復現(xiàn)
2.1 業(yè)務場景
需通過管理員ID查詢該管理員的基本信息、關聯(lián)的角色列表,以及每個角色對應的權限列表,涉及admin(管理員表)、role(角色表)、permission(權限表)三張核心表,通過中間表admin_role和role_permission實現(xiàn)關聯(lián)。
2.2 核心代碼實現(xiàn)
1. Mapper.xml查詢SQL
SELECT
a.id,
a.username,
r.id,
r.role_name,
r.role_desc,
p.id,
p.permission_name,
p.url
FROM
`admin` a
LEFT JOIN admin_role ON a.id = admin_role.aid
LEFT JOIN `role` r ON admin_role.rid = r.id
LEFT JOIN role_permission ON r.id = role_permission.rid
LEFT JOIN permission p ON role_permission.pid = p.id
WHERE
a.id = #{aid}直接在數(shù)據(jù)庫執(zhí)行該SQL,返回2條結果(對應2個權限),數(shù)據(jù)正常。
2. ResultMap配置
<resultMap id="adminMapper" type="com.lion.online.pojo.Admin">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<!-- 角色列表關聯(lián) -->
<collection property="roles" column="id" ofType="com.lion.online.pojo.Role">
<id property="id" column="id"></id>
<result property="roleName" column="role_name"></result>
<result property="roleDesc" column="role_desc"></result>
<!-- 權限列表關聯(lián) -->
<collection property="permissions" column="id" ofType="com.lion.online.pojo.Permission">
<id property="id" column="id"/>
<result property="permissionName" column="permission_name"></result>
<result property="url" column="url"></result>
</collection>
</collection>
</resultMap>3. 異常結果
項目運行后,控制臺打印SQL執(zhí)行結果為2條,但頁面僅顯示1條權限數(shù)據(jù),List集合長度異常為1。
三、BUG根源分析
3.1 核心問題:字段名沖突
MyBatis的ResultMap中,id標簽用于標識實體類的主鍵字段,其column屬性對應查詢結果集中的列名(而非數(shù)據(jù)庫表字段名)。本案例中:
- 管理員表、角色表、權限表的主鍵均命名為
id - 查詢SQL未對多表中的
id字段起別名,導致結果集中出現(xiàn)多個同名id列 - MyBatis在映射數(shù)據(jù)時,無法區(qū)分主表(admin)、關聯(lián)表(role、permission)的
id字段,誤將所有id列值當作主表主鍵處理,最終只保留了一條去重后的數(shù)據(jù)
3.2 關鍵原理
ResultMap的id標簽具有"數(shù)據(jù)唯一標識"的作用,MyBatis會根據(jù)id字段的值判斷是否為同一實體。
當多表id字段同名且未區(qū)分時,MyBatis會認為所有關聯(lián)數(shù)據(jù)屬于同一個實體,從而覆蓋重復數(shù)據(jù),導致最終返回結果條數(shù)少于實際查詢結果。
四、解決方案
核心思路:通過SQL別名區(qū)分多表中的同名id字段,在ResultMap中明確映射對應關系。
4.1 優(yōu)化查詢SQL(添加字段別名)
對角色表id起別名rid,權限表id起別名pid,避免列名沖突:
SELECT
a.id,
a.username,
r.id AS rid, -- 角色ID別名
r.role_name,
r.role_desc,
p.id AS pid, -- 權限ID別名
p.permission_name,
p.url
FROM
`admin` a
LEFT JOIN admin_role ON a.id = admin_role.aid
LEFT JOIN `role` r ON admin_role.rid = r.id
LEFT JOIN role_permission ON r.id = role_permission.rid
LEFT JOIN permission p ON role_permission.pid = p.id
WHERE
a.id = #{aid}4.2 修正ResultMap配置(映射別名列)
更新ResultMap中collection標簽的column屬性,指向SQL中定義的別名:
<resultMap id="adminMapper" type="com.lion.online.pojo.Admin">
<!-- 管理員主表映射 -->
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<!-- 角色列表關聯(lián):column指向角色ID別名rid -->
<collection property="roles" column="rid" ofType="com.lion.online.pojo.Role">
<id property="id" column="rid"></id> <!-- 角色主鍵映射別名列 -->
<result property="roleName" column="role_name"></result>
<result property="roleDesc" column="role_desc"></result>
<!-- 權限列表關聯(lián):column指向權限ID別名pid -->
<collection property="permissions" column="pid" ofType="com.lion.online.pojo.Permission">
<id property="id" column="pid"/> <!-- 權限主鍵映射別名列 -->
<result property="permissionName" column="permission_name"></result>
<result property="url" column="url"></result>
</collection>
</collection>
</resultMap>4.3 優(yōu)化說明
- SQL別名命名規(guī)范:建議采用"表名首字母+id"的格式(如
rid=role_id、pid=permission_id),提高可讀性 collection標簽的column屬性:需與SQL別名完全一致,確保MyBatis能正確匹配關聯(lián)數(shù)據(jù)- 主鍵映射優(yōu)先級:
id標簽的映射優(yōu)先級高于result標簽,必須優(yōu)先確保主鍵字段映射正確
五、驗證結果
優(yōu)化后重新運行項目,頁面成功展示所有關聯(lián)數(shù)據(jù):
List集合長度與數(shù)據(jù)庫查詢結果一致,BUG修復完成。
六、避坑總結
- 多表聯(lián)查必注意:只要涉及多張表,必須對同名字段起別名,尤其是主鍵
id - ResultMap映射原則:
column屬性對應"查詢結果集的列名",而非數(shù)據(jù)庫表字段名,別名變更需同步更新
七、總結
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
Java中Spock框架Mock對象的方法經(jīng)驗總結
這篇文章主要分享了Spock框架Mock對象的方法經(jīng)驗總結,下文分享一些常用項目實戰(zhàn)說明以及代碼,供大家項目中參考,也具有一的的參考價值,需要的小伙伴可以參考一下2022-02-02
Linux系統(tǒng)下Java部署環(huán)境搭建與項目部署超詳細步驟
這篇文章主要給大家介紹了關于Linux系統(tǒng)下Java部署環(huán)境搭建與項目部署的超詳細步驟,通過示例講解了JDK和MySQL安裝、項目打包上傳、后臺啟動、防火墻配置及進程管理,提供XShell和lrzsz等工具使用方法,需要的朋友可以參考下2025-07-07
SpringMVC中的@RequestMapping注解的使用詳細教程
@RequestMapping注解的作用就是將請求和處理請求的控制器方法關聯(lián)起來,建立映射關系,本文主要來和大家詳細講講它的具體使用,感興趣的可以了解一下2023-07-07
Springboot前后端分離項目配置跨域實現(xiàn)過程解析
這篇文章主要介紹了Springboot前后端分離項目配置跨域實現(xiàn)過程解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-08-08
TransactionSynchronization的invokeAfterCompletion事務源碼解析
這篇文章主要為大家介紹了TransactionSynchronization的invokeAfterCompletion事務源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-09-09

