MyBatis @MapKey 注解详解

🎯 一句话总结

@MapKey 告诉 MyBatis:把查询结果的某一列作为 Map 的 key,整行数据作为 value。


📖 为什么需要 @MapKey?

场景:查询多条记录,想通过某个字段快速查找

假设你有以下数据:

id name email
1 张三 zhang@example.com
2 李四 li@example.com
3 王五 wang@example.com

你想通过 id 快速找到对应的用户。


❌ 没有 @MapKey 的情况

Mapper 接口

java 复制代码
List<Map<String, Object>> queryUsers();

返回结果

java 复制代码
[
  {id=1, name=张三, email=zhang@example.com},
  {id=2, name=李四, email=li@example.com},
  {id=3, name=王五, email=wang@example.com}
]

使用方式(麻烦)

java 复制代码
List<Map<String, Object>> list = mapper.queryUsers();

// 想找到 id=2 的用户?需要遍历!
Map<String, Object> target = null;
for (Map<String, Object> row : list) {
    if (Integer.valueOf(2).equals(row.get("id"))) {
        target = row;
        break;
    }
}

问题: 每次查找都要遍历整个列表,性能差!


✅ 有 @MapKey 的情况

Mapper 接口

java 复制代码
@MapKey("id")  // ← 指定用 id 作为 key
Map<String, Map<String, Object>> queryUsers();

返回结果

java 复制代码
{
  "1": {id=1, name=张三, email=zhang@example.com},
  "2": {id=2, name=李四, email=li@example.com},
  "3": {id=3, name=王五, email=wang@example.com}
}

使用方式(简单)

java 复制代码
Map<String, Map<String, Object>> map = mapper.queryUsers();

// 想找到 id=2 的用户?直接 get!
Map<String, Object> target = map.get("2");

优势: O(1) 时间复杂度,瞬间找到!


🔍 @MapKey 的工作原理

形象比喻

想象你有一堆名片:

没有 @MapKey:

复制代码
名片堆在一起,想找"张三"的名片?
→ 必须一张张翻,直到找到为止 😫

有 @MapKey:

复制代码
名片按名字排好序放在抽屉里,想找"张三"?
→ 直接打开"张"字抽屉,瞬间拿到 😊

技术实现

java 复制代码
@MapKey("id")
Map<String, Map<String, Object>> queryUsers();

MyBatis 会:

  1. 执行 SQL,得到多行结果
  2. 遍历每一行
  3. 取出该行的 id 列的值(如 "1"、"2"、"3")
  4. 把这一整行数据作为 value
  5. 组成 Map<"1", 该行数据>、Map<"2", 该行数据>...

📊 对比总结

特性 没有 @MapKey 有 @MapKey
返回类型 List<Map<K,V>> Map<String, Map<K,V>>
查找方式 遍历列表 直接 get(key)
时间复杂度 O(n) O(1)
代码可读性 差 好
适用场景 只需要遍历 需要通过某列快速查找

🎓 常见误区

误区 1:@MapKey 只能用于 Map 返回值

❌ 错误理解: @MapKey 只能配合 Map 返回类型使用

✅ 正确理解: @MapKey 可以用于任何返回多条记录的查询,包括:

  • Map<String, Object> - 单条记录
  • Map<String, Map<String, Object>> - 多条记录
  • Map<String, YourDO> - 多条记录映射到对象

误区 2:@MapKey 的值可以是任意字段

❌ 错误用法:

java 复制代码
@MapKey("non_existent_column")  // 这个字段在 SQL 中不存在

✅ 正确用法:

java 复制代码
@MapKey("id")  // 必须是 SELECT 中的列名

误区 3:@MapKey 会自动去重

❌ 如果有重复的 key:

sql 复制代码
SELECT id, ... FROM table
-- 结果:
-- 1
-- 1  ← 重复!

后面的会覆盖前面的!

✅ 解决方案:

sql 复制代码
SELECT DISTINCT id, ... FROM table

🚀 最佳实践

1. 明确是否需要快速查找

java 复制代码
// 只需要遍历 → 不需要 @MapKey
List<User> list = mapper.selectAll();
for (User u : list) { ... }

// 需要通过 ID 查找 → 需要 @MapKey
@MapKey("id")
Map<String, User> map = mapper.selectAll();
User u = map.get("123");

2. 选择合适的 key 字段

java 复制代码
// ✅ 好的选择:唯一、非空、简短
@MapKey("id")
@MapKey("user_code")

// ❌ 不好的选择:可能重复、可能为空、太长
@MapKey("description")
@MapKey("email")  // 邮箱可能很长

3. 配合 Java 8 Stream 使用

java 复制代码
// 如果 Mapper 返回 List,可以用 Stream 转 Map
List<User> list = mapper.selectAll();
Map<String, User> map = list.stream()
    .collect(Collectors.toMap(User::getId, u -> u));

💬 总结

@MapKey 的本质:

把数据库查询结果从"列表"变成"字典",让你可以通过某个字段快速查找记录。

什么时候用:

  • ✅ 需要通过某列快速查找
  • ✅ 避免多次遍历列表
  • ✅ 提高代码可读性

什么时候不用:

  • ❌ 只需要遍历所有数据
  • ❌ 只有一条记录
  • ❌ key 字段不唯一
相关推荐
2601_9628857211 小时前
AkShare 和 Tushare 哪个好?区别、场景与选型
mybatis
云和数据.ChenGuang13 小时前
python sorted函数详细剖析
开发语言·人工智能·windows·python·1024程序员节
小熊吃保安15 小时前
Windows也能用 Apple Watch 解锁
windows·rust·开源
fengye20716115 小时前
Windows 环境下硬核编译 CPython 避坑指南
windows
成成爱质评18 小时前
Windows 任务计划程序 + 唤醒定时器:让电脑按时开机干活
windows·单片机·电脑
禁默18 小时前
iPhone远程控制Windows电脑哪款好?四款工具实测横评
windows·电脑·iphone
_Evan_Yao1 天前
Redis 缓存穿透、击穿、雪崩,一次讲清!
java·spring·mybatis
Sakura_j_h_1 天前
Python 在定时任务里输出 emoji,UnicodeEncodeError 从哪来
开发语言·字符编码·windows·python·定时任务
paopaokaka_luck1 天前
宠物门店经营管理系统(销售与库存联动、进货自动记账、 退换与退款关联、员工个人业绩、ECharts 经营分析、滞销识别与客户分层、宠物档案年龄计算)
前端·javascript·spring boot·mysql·echarts·mybatis·1024程序员节
隔窗听雨眠1 天前
PostgreSQL单表从Windows迁移到Linux:差异剖析、方案对比与全流程实战
linux·windows·postgresql