mybatis学习之查询结果返回List以及Map

当要查询的结果不是单个记录而是一个集合的时候,mybatis的resultType写的依然是集合中的对象的类型名称,例如:

    <select id="getPersonByNameLike"resultType="mybatis_01.Person">
        select * from person where name like #{name}
    </select>

该模糊查询查出的是一个List集合,但是resultType写的是集合中元素的类型

将查询结果封装成Map的两种情况:
1.若查询的结果是单条记录,采用如下的方式:

<select id="getPersonByIdReturnMap" resultType="map">
        select * from person where id=#{id};
</select>

注意,此时的resultType写的是map,这是以为mybatis为这些集合起了别名

2.若查询的结果是多条记录时,要将每条记录都封装成Map,需要使用以下方式:

<select id="getPersonByNameLikeReturnMap" resultType="mybatis_01.Person">
        select * from person where name like #{name};
</select>

此时resultType是Map中value的类型,使用注解方式指定Map的key
@MapKey

@MapKey("id")
    public Map<Integer, Person> getPersonByNameLikeReturnMap(String name);

以上的方法返回的Map的key就是Person的id,Value就是这个Person对象

<think>我们被要求使用MyBatis实现查询返回类型为`Map<String,List<?>>`。这意味着我们需要将查询结果组织成一个映射,其中键是某个字符串(例如,某个字段的值),值是对应这个键的多个记录的列表。根据引用[1]引用[3],我们知道MyBatis可以返回MapList<Map>,但是这里我们需要的是更复杂的结构:`Map<String,List<?>>`。MyBatis本身并没有直接提供这种返回类型的支持,因此我们需要在Java代码中进行转换。不过,我们可以利用MyBatis查询结果返回一个列表(例如`List<SomeObject>`或者`List<Map<String,Object>>`),然后在服务层或DAO层将这个列表转换成`Map<String,List<?>>`的形式。假设我们有一个需求:根据部门(department)分组,返回每个部门下的所有员工。那么,我们希望得到一个Map,其中键是部门名称(String),值是该部门下的员工列表(List<Employee>)。有两种常见方法:1.查询返回一个包含所有员工的列表(例如`List<Employee>`),然后在Java代码中通过流操作或循环进行分组。2.使用MyBatis的@MapKey注解,但注意@MapKey只能将结果映射为Map<Key,Value>,其中Value是单个记录,而不是列表。所以我们需要的是分组,@MapKey并不直接支持分组(它返回的是每个键对应的一个对象,如果键重复,后面的会覆盖前面的)。因此,我们通常采用第一种方法:先查询出所有记录,然后在Java中分组。但是,如果我们希望直接在MyBatis中实现分组查询返回Map<String,List>,我们可以使用MyBatis的resultMap嵌套集合(collection)来实现,但这样返回的是一个包含分组结构的自定义对象(或Map),而不是直接的Map<String,List>。而且,这种方式通常用于关联查询,并且返回的是一个包含多个分组的对象(例如一个包含多个部门的列表,每个部门有一个员工列表),而不是一个Map。因此,更简单直接的方式是:步骤1:在MyBatis查询出所有记录(例如,每个员工的信息,包括他所在的部门)。步骤2:在Java代码中,遍历这个列表,按照部门名称分组,形成`Map<String,List<Employee>>`。示例:假设我们有一个员工表(employee),字段包括:id,name,department。1.创建Employee类:```javapublicclassEmployee{privateIntegerid;privateStringname;privateStringdepartment;//省略gettersetter}```2.在Mapper接口中定义查询方法,返回所有员工的列表:```javapublicinterfaceEmployeeMapper{List<Employee>findAllEmployees();}```3.在MapperXML文件中编写SQL:```xml<selectid="findAllEmployees"resultType="com.example.Employee">SELECTid,name,departmentFROMemployee</select>```4.在Service层进行分组:```java@ServicepublicclassEmployeeService{@AutowiredprivateEmployeeMapperemployeeMapper;publicMap<String,List<Employee>>getEmployeeGroupByDepartment(){List<Employee>allEmployees=employeeMapper.findAllEmployees();//使用Java8的流操作进行分组Map<String,List<Employee>>groupedByDepartment=allEmployees.stream().collect(Collectors.groupingBy(Employee::getDepartment));returngroupedByDepartment;}}```如果我们不想定义Employee类,而是直接返回`Map<String,List<Map<String,Object>>>`,那么可以这样做:1.Mapper接口:```javaList<Map<String,Object>>findAllEmployeesAsMap();```2.MapperXML:```xml<selectid="findAllEmployeesAsMap"resultType="java.util.Map">SELECTid,name,departmentFROMemployee</select>```3.Service层:```javapublicMap<String,List<Map<String,Object>>>getEmployeeMapGroupByDepartment(){List<Map<String,Object>>allEmployees=employeeMapper.findAllEmployeesAsMap();//注意:这里我们假设每个员工的Map中都有"department"这个键Map<String,List<Map<String,Object>>>result=allEmployees.stream().collect(Collectors.groupingBy(empMap->(String)empMap.get("department")));returnresult;}```但是,请注意,这种方式在数据量大的时候可能会存在性能问题,因为我们需要先查询出所有数据再在内存中分组。如果数据量很大,建议直接在SQL中进行分组,然后通过多次查询(先查所有部门,再根据每个部门查员工)或者使用存储过程,但这通常不是MyBatis的典型用法。另外,如果我们要在MyBatis中直接返回一个Map<String,List<Map>>(即一次查询就得到分组结构),我们可以使用自定义ResultHandler,但这种方式相对复杂。总结:对于`Map<String,List<?>>`这种返回类型,我们通常的做法是先查询出列表,然后在Java代码中分组。因此,示例代码将分为两部分:MyBatis查询列表(可以是对象列表或Map列表)Java代码中的分组操作。下面我们给出一个完整的示例,从Mapper接口到Service层的代码。注意:如果用户希望直接通过一次MyBatis查询得到这样的结构,那么可能需要使用@MapKey嵌套查询,但这样会使得MyBatis配置复杂,且可能效率不高(N+1查询问题)。所以这里我们采用先查询后分组的方式。参考引用[2],我们可以使用resultMap来映射复杂结构,但这里我们不需要映射成List<Map>,因为分组操作在Java中更灵活。因此,我们给出以下示例:</think>### MyBatis 查询返回 `Map<String, List>` 的完整实现 #### 核心思路 MyBatis 原生不支持直接返回 `Map<String, List<?>>` 结构,需结合以下技术实现: 1. **查询基础数据**:使用 `List<Map<String, Object>>` 存储原始记录 2. **Java 流处理**:对结果集进行分组聚合 3. **自定义结果处理器**(可选):处理复杂分组逻辑 --- ### 方法一:Service 层分组(推荐) #### 1. Mapper 接口定义 ```java // 返回原始记录集 @Select("SELECT department, name, salary FROM employees") List<Map<String, Object>> selectEmployees(); ``` #### 2. Service 层处理分组 ```java public Map<String, List<Map<String, Object>>> getEmployeeGroup() { List<Map<String, Object>> rawList = employeeMapper.selectEmployees(); // 使用 Java Stream 按部门分组 return rawList.stream() .collect(Collectors.groupingBy( record -> (String) record.get("department"), Collectors.mapping(record -> { Map<String, Object> emp = new HashMap<>(); emp.put("name", record.get("name")); emp.put("salary", record.get("salary")); return emp; }, Collectors.toList()) )); } ``` #### 输出结构示例 ```json { "Engineering": [ {"name": "Alice", "salary": 9000}, {"name": "Bob", "salary": 8500} ], "Marketing": [ {"name": "Charlie", "salary": 8000} ] } ``` --- ### 方法二:自定义 ResultHandler(高性能方案) 适用于大数据量场景,直接在结果集处理时分组 #### 1. 实现 ResultHandler ```java public class GroupResultHandler implements ResultHandler<Map<String, Object>> { private final Map<String, List<Map<String, Object>>> resultMap = new HashMap<>(); @Override public void handleResult(ResultContext<? extends Map<String, Object>> context) { Map<String, Object> record = context.getResultObject(); String key = (String) record.get("department"); // 提取需要的字段 Map<String, Object> value = new HashMap<>(); value.put("name", record.get("name")); value.put("salary", record.get("salary")); // 分组存储 resultMap.computeIfAbsent(key, k -> new ArrayList<>()).add(value); } public Map<String, List<Map<String, Object>>> getResult() { return resultMap; } } ``` #### 2. Mapper 接口调用 ```java void selectEmployeesWithHandler(ResultHandler handler); ``` #### 3. Service 层调用 ```java public Map<String, List<Map<String, Object>>> getEmployeeGroup() { GroupResultHandler handler = new GroupResultHandler(); employeeMapper.selectEmployeesWithHandler(handler); return handler.getResult(); } ``` --- ### XML 配置要点 ```xml <!-- 方法一的基础查询配置 --> <select id="selectEmployees" resultType="java.util.Map"> SELECT department, name, salary FROM employees </select> <!-- 方法二的自定义处理器配置 --> <select id="selectEmployeesWithHandler" resultType="java.util.Map" fetchSize="1000"> SELECT department, name, salary FROM employees </select> ``` --- ### 关键注意事项 1. **性能考量**: - 小数据量(<1000条):优先用 Service 层分组 - 大数据量:使用 `ResultHandler` 避免内存溢出[^4] 2. **字段控制**: - SQL 中明确指定字段(避免 `SELECT *`) - 分组字段需保证非空(如 `department`) 3. **类型安全**: - 强制转换前检查字段存在性:`record.containsKey("department")` - 使用 `@MapKey` 注解处理单级 Map(不适用本场景)[^1] 4. **空值处理**: - 使用 `java.util.Optional` 包装可能为空的字段 - 在分组前过滤空键:`.filter(record -> record.get("department") != null)` --- ### 典型应用场景 1. 部门-员工列表 2. 分类-商品列表 3. 日期-交易记录 4. 城市-用户分布 > 通过合理的分组处理,MyBatis 可实现灵活的 `Map<String, List>` 结构返回,满足树形数据展示需求[^2][^3]。 --- ### 相关问题 1. MyBatis 如何实现结果集的动态字段映射? 2. 大数据量下 MyBatis 分页查询的最佳实践是什么? 3. 怎样优化 MyBatis 返回 Map 类型时的内存占用? 4. MyBatis 的 `@MapKey` 注解适用于哪些场景?
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值