@MapKey映射Java对象属性名(如"code"),要求该属性值唯一,否则后值覆盖前值;非主键也可用,但重复会导致数据丢失。

MyBatis查多条记录时,@MapKey到底映射哪一列?
它只认你指定的字段名(或属性名),且该字段值必须唯一,否则后插入的会覆盖前面的。不是主键也能用,但重复就丢数据。
常见错误现象:Map里只有 1 条记录,或者 key 看起来是乱序的——其实只是被反复覆盖了。
- 使用场景:查配置表、字典表、状态码映射等「ID/Code → 实体」关系
- 参数差异:
@MapKey("code")中的code是 Java 对象的属性名,不是数据库列名(除非开启mapUnderscoreToCamelCase) - 如果数据库列是
status_code,而实体属性是statusCode,确保开启了驼峰转换,否则@MapKey("statusCode")找不到值,整个 Map 可能为 null
不加 @MapKey 直接返回 Map<String, Object> 怎么写?
这是最轻量的“无映射”方式,适合动态字段、临时解析、SQL 不固定等场景,但你要自己处理类型和空值。
常见错误现象:数字字段变成 Long 或 BigDecimal,日期变成 Timestamp,取值时 map.get("user_name") 拼错 key 就是 null。
- Mapper 接口方法返回类型写成
List<Map<String, Object>>,不是Map<String, Object>(后者只适合单条) - XML 中不用写
resultMap,直接用resultType="map" - 注意 JDBC 驱动行为:MySQL 8.0+ 默认把整数映射为
Long,PostgreSQL 可能返回Integer,别硬转int
resultMap + collection 嵌套查出 Map 结构?
不能。MyBatis 的 <collection> 只支持封装为 List 或数组,不支持直接塞进 Map。想实现「一对多 → 某个字段为 key 的 Map」,得靠业务层收口。
常见错误现象:在 <collection> 里写 javaType="map",启动报错 Invalid bound statement (not found) 或运行时报 ClassCastException。
- 正确做法:先用
resultMap映射出带子列表的父对象,再在 Service 层用Collectors.toMap()转一次 - 如果子项本身要按某字段索引(比如
orderItems按skuId归类),别指望 XML 一步到位 - 性能影响:嵌套查询 + 后续流式转换,比单 SQL +
@MapKey多一次遍历,大数据量时注意 GC 压力
为什么 @MapKey 在接口方法上加了却没生效?
因为 MyBatis 只在返回类型是 Map 且方法是 SELECT 时才处理它;如果返回的是 Map<String, User> 但 XML 里写了 resultMap,注解会被忽略。
常见错误现象:断点调试发现返回的是 LinkedHashMap,但 key 是默认的 0, 1, 2 ——说明压根没走 @MapKey 逻辑。
- 必须同时满足:返回类型是
Map<?, ?>、没有配置resultMap、SQL 查的是多行 - Spring Boot 项目里,如果用了
mybatis-spring-boot-starter2.3.0+,确认没开启configuration.map-underscore-to-camel-case=false导致 key 匹配失败 - 最容易被忽略的一点:Mapper 方法不能有多个参数(哪怕用
@Param),否则代理逻辑可能绕过注解解析
复杂点在于,@MapKey 是运行时靠反射 + 字节码增强介入的,一旦混合了自定义 TypeHandler 或泛型擦除严重,就容易静默失效。遇到问题优先检查返回类型声明和 XML 是否干净。










