MyBatis使用@MapKey注解

本文详细介绍了在MyBatis中如何使用@MapKey注解将查询结果存储为Map集合,包括其背景、含义及具体示例。通过Mapper XML配置与DAO层的配合,实现将多表查询结果映射到Map中,便于进一步的数据处理。


一. 背景和含义

  1. 背景:今天使用MyBatis查询一些记录,数据涉及到两个表里的数据,需要连表查询,但我MyBatis的返回结果不想创建新的DO对象,因此使用@MapKey注解返回一个Map集合。
  2. 含义:@MapKey注解用于mapper.xml文件中,一般用于查询多条记录中各个字段的结果,存储在Map中。Map结构的示例如下:
    Map<Long, Map<String, String>>。范型类型可以修改。
    • Map的key:一般存储每条记录的主键,也可以用其他值表示,主要取决于Dao层@MapKey注解后面的字段(如@MapKey("id"));
    • Map的value:也是一个Map,表示查询出这条记录的每个字段的字段名称和字段值。

二. 具体示例

1. mapper.xml文件

<select id="getInfoById" resultType="java.util.Map">
	select
	ext.id as id,
	info.produce_area as area,
	ext.attribute_value as companyName
	from product_info info
	left join product_ext ext on info.id=ext.id
	where info.id=#{id} and ext.attribute_name=#{name}
</select>

2. dao.java

@MapKey("id")
Map<Long,Map<String,String>> getInfoById(@Param("id") Long id, @Param("name") String name);

3. manager.java

// 查询,返回结果:{1={area:上海,companyName=西西公司}}
// 如果是多条记录,返回结果如:{1={area:上海,companyName:西西公司},2={area:南京,compantyName:猴猴公司}}
Map<Long, Map<String, String>> map = productDao.getInfoById(id, "公司名称"); 
if(map==null || map.isEmpty() || !map.containsKey(id)){
    return null;
}

// 获取结果
String area = produceInfoMap.get(id).get("area");
String companyName = produceInfoMap.get(productTracInfoId).get("companyName");

参考资料

MyBatis使用@MapKey注解接收多个查询记录到Map中

<think>我们被要求解释Mybatis @MapKey注解的用法。根据引用内容,我们可以总结如下: @MapKey注解用于MyBatis的Mapper接口方法上,其作用是当返回类型为Map时,指定使用结果对象的哪个属性作为Map的键(Key)。这样,MyBatis在查询后会将结果集按照指定的键组织成一个Map,其中键是唯一的(如果不唯一,后面的记录会覆盖前面的记录),值则是整个结果对象(或查询结果映射的对象)。 关键点: 1. 使用位置:Mapper接口的方法上。 2. 作用:指定Map的键(Key)对应的属性。 3. 要求:指定的属性在结果对象中必须存在,且必须唯一(否则会出现覆盖)。 4. 返回类型:方法的返回类型必须是Map(如Map<K, V>)。 示例: ```java @MapKey("id") // 指定使用查询结果中对象的id属性作为Map的键 @Select("SELECT id, name FROM user") Map<Integer, User> getUserMap(); ``` 在这个例子中,MyBatis执行查询后,会将每个User对象的id作为键,User对象本身作为值,放入Map中返回。 另外,引用[3]提到,如果不使用@MapKey注解,当返回类型为Map时,MyBatis默认会将结果集按照列名进行映射,并且返回一个Map<String, Object>(每个记录对应一个Map),但这样返回的是多个Map(每个记录一个Map),而如果我们要将多个记录放入一个Map中并以某个字段为键,就需要使用@MapKey。 引用[4]展示了MyBatis源码中如何获取@MapKey注解的值:通过方法上的@MapKey注解,获取其value,然后作为Map的键的属性名。 因此,@MapKey注解的用法总结如下: - 在Mapper接口的方法上添加@MapKey("字段名"),字段名是查询结果对象中的属性名。 - 方法的返回类型写为Map<键类型, 值类型>,其中键类型就是字段名对应的属性类型,值类型是结果对象的类型(如果查询返回的是单个对象属性,也可以是属性的类型,但通常返回整个对象)。 注意:如果查询结果中指定的键不唯一,则后面的记录会覆盖前面的记录,所以确保键唯一非常重要。 另外,引用[1]和[2]也提供了类似的用法说明。 下面我们通过一个例子来详细说明: 假设有一个User表,有id和name两个字段,对应的实体类为User。我们想查询所有用户,并以id为键,User对象为值,返回一个Map。 1. 定义Mapper接口方法: ```java @MapKey("id") // 指定使用id属性作为Map的键 @Select("SELECT * FROM user") Map<Integer, User> getUserMap(); ``` 2. 调用该方法后,返回的Map结构如下: - 键:每个用户的id(整数类型) - 值:对应的User对象 3. 使用示例: ```java Map<Integer, User> userMap = userMapper.getUserMap(); User user = userMap.get(1); // 获取id为1的用户 ``` 如果查询结果中,有两个用户的id相同(比如都是1),那么后一个用户会覆盖前一个用户,导致前一个用户数据丢失。 另外,引用[3]提到,还可以模拟复合键(多个字段组合作为键),但MyBatis@MapKey本身不支持指定多个属性。我们可以通过以下方式模拟: - 在查询语句中使用连接符将多个字段连接成一个字符串(如CONCAT(col1, '-', col2)),然后使用这个字符串作为键。但这样就需要在实体类中增加一个对应的属性(或者使用别名),然后用这个别名作为@MapKey的value。 - 或者,返回一个自定义对象作为键,但这样需要自定义结果处理器(ResultHandler)或者使用MyBatis的@ResultMap注解进行复杂映射,相对复杂。 所以,@MapKey注解主要适用于单字段作为键的情况。 最后,需要注意:@MapKey注解只能用于返回类型为Map的方法,不能用于返回单个对象或者List的方法。 相关问题: 1. 使用@MapKey注解时,如果指定的属性在结果集中不存在会怎样? 2. 如何避免@MapKey注解导致的键重复覆盖问题? 3. 除了@MapKey注解MyBatis还有哪些处理结果集为Map的方式? 4. 在动态SQL查询中,@MapKey注解使用有什么限制? </think> ### MyBatis `@MapKey` 注解用法详解 `@MapKey` 是 MyBatis 框架中用于处理查询结果映射为 Map 集合的关键注解,主要作用是指定结果集中 **某个属性作为 Map 的键**。以下是核心用法和规则: --- #### 一、基础用法 1. **注解位置**:Mapper 接口方法上 2. **核心功能**:将查询结果转为 `Map<K, V>` 结构 - 键 (`K`):由 `@MapKey` 指定的属性值 - 值 (`V`):每行查询结果对象 ```java // 示例:以用户ID为键,用户对象为值 @MapKey("id") // 指定id作为Map的键 @Select("SELECT id, name, email FROM user") Map<Integer, User> getUserMap(); // Map<id, User> ``` 3. **执行结果**: ```java { 1: User{id=1, name="Tom", email="tom@example.com"}, 2: User{id=2, name="Jerry", email="jerry@example.com"}, ... } ``` --- #### 二、核心规则 1. **键属性要求**: - 必须是查询结果中的字段或属性(如 `id`) - 需保证**唯一性**(重复键会导致数据覆盖)[^3] - 支持基本类型(`Integer`, `Long`)和字符串(`String`) 2. **返回值类型**: - 方法返回类型必须是 `Map` 或其子类 - 值类型可以是: - 实体类(如 `User`) - 简单类型(如 `Map<String, String>`) - 自定义 DTO 3. **无注解时风险**: ```java // 未使用@MapKey时,MyBatis默认返回List<Map>结构 @Select("SELECT id, name FROM user") List<Map<String, Object>> getUsers(); // 每行一个Map,非单Map结构 ``` --- #### 三、进阶用法 1. **统计场景**(键为分组字段,值为统计值): ```java @MapKey("parentId") // 指定parentId为键 @Select("SELECT parent_id, COUNT(*) AS count FROM dim_tree_node_t GROUP BY parent_id") Map<Long, Long> countByParentId(); // Map<parentId, count> ``` 2. **复合键模拟**: ```java // 使用SQL拼接构造复合键 @MapKey("fullKey") @Select("SELECT CONCAT(type,'_',status) AS fullKey, * FROM config") Map<String, Config> getConfigMap(); // Map<"type_status", Config> ``` 3. **动态 SQL 配合**: ```xml <!-- XML中定义SQL --> <select id="getUserMap" resultType="map"> SELECT id, name FROM user <where> <if test="role != null">AND role = #{role}</if> </where> </select> ``` ```java // Mapper接口 @MapKey("id") Map<Integer, User> getUserMap(@Param("role") String role); ``` --- #### 四、常见错误 1. **键不唯一**:导致数据覆盖(如多个用户同名) ```java @MapKey("name") // 风险:同名用户会被覆盖! Map<String, User> getUserMapByName(); ``` 2. **属性不存在**:抛出 `BindingException` ```java @MapKey("invalidField") // 错误:查询结果无此字段 Map<String, User> getUserMap(); ``` 3. **类型不匹配**:键/值类型与声明不符 ```java @MapKey("id") Map<String, User> getUserMap(); // 错误:id是Long型,但键声明为String ``` --- #### 五、最佳实践 1. **键选择原则**:优先使用主键或唯一约束字段 2. **大数据量优化**:结合分页避免内存溢出 3. **空值处理**:添加 `WHERE key_field IS NOT NULL` 4. **结果验证**:通过 `Map.size()` 检查条目数是否匹配预期 > 通过合理使用 `@MapKey`,可高效实现 **行转列**、**分组统计**等场景,简化数据转换逻辑[^2][^3]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值