You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用MyBatis统计行数并将结果转为Map<String, Long>?

解决MyBatis将GROUP BY查询结果转为Map<String, Long>的问题

先给你拆解下你遇到的两个问题,再给出针对性的解决方案:

问题1:使用resultType="hashmap" + selectOne()抛出TooManyResultsException

这个异常的原因很明确:你的GROUP BY查询返回了3条不同state的统计记录,但selectOne()方法要求MyBatis只能返回0条或1条结果,而你把多条记录丢给它处理,自然就报错了。resultType="hashmap"只是告诉MyBatis把每条记录转成HashMap,但selectOne()无法处理多条结果集合。

问题2:使用@MapKey后遇到JsonGenerationException(Null相关)

这个大概率是因为你的state字段存在NULL值——当你用NULL作为Map的key时,后续的JSON序列化(比如接口返回给前端)会因为JSON规范不允许null作为键而抛出异常。


正确的解决方案(XML映射器场景)

根据你的需求,我推荐两种可行的方式,你可以根据场景选择:

方式1:用DTO配合@MapKey实现优雅转换

这种方式最符合MyBatis的最佳实践,可读性和维护性都很好:

  1. 定义一个简单的DTO类,用来封装每条GROUP BY的结果:
public class StateOrderCount {
    private String state;
    private Long count;

    // 必须添加getter方法(MyBatis需要通过反射读取字段值)
    public String getState() { return state; }
    public void setState(String state) { this.state = state; }
    public Long getCount() { return count; }
    public void setCount(Long count) { this.count = count; }
}
  1. 在Mapper接口方法上添加@MapKey注解,指定用state字段作为Map的key,方法返回Map<String, StateOrderCount>:
import org.apache.ibatis.annotations.MapKey;

public interface OrderMapper {
    @MapKey("state")
    Map<String, StateOrderCount> getStateOrderCountMap();
}
  1. XML映射文件中编写查询语句,指定resultType为你定义的DTO类,同时处理null值:
<select id="getStateOrderCountMap" resultType="com.yourpackage.StateOrderCount">
    <!-- 用COALESCE处理state为NULL的情况,避免后续JSON序列化报错 -->
    SELECT COALESCE(state, 'UNKNOWN') AS state, COUNT(state) AS count 
    FROM orders 
    GROUP BY state;
</select>
  1. 如果需要直接得到Map<String, Long>,可以在业务层做一次轻量转换:
Map<String, StateOrderCount> tempMap = orderMapper.getStateOrderCountMap();
Map<String, Long> finalMap = new HashMap<>();
tempMap.forEach((key, value) -> finalMap.put(key, value.getCount()));

方式2:直接用List+手动转换(无需DTO)

如果不想定义DTO,也可以直接查询得到List,再手动转成目标Map:

  1. Mapper接口定义方法:
public interface OrderMapper {
    List<Map<String, Object>> getStateOrderCountList();
}
  1. XML映射文件:
<select id="getStateOrderCountList" resultType="hashmap">
    SELECT COALESCE(state, 'UNKNOWN') AS state, COUNT(state) AS count 
    FROM orders 
    GROUP BY state;
</select>
  1. 业务层转换:
List<Map<String, Object>> resultList = orderMapper.getStateOrderCountList();
Map<String, Long> stateCountMap = new HashMap<>();
for (Map<String, Object> item : resultList) {
    String state = (String) item.get("state");
    // 这里因为SQL里用了COALESCE,state不会为null,所以可以直接强转
    Long count = ((Number) item.get("count")).longValue();
    stateCountMap.put(state, count);
}

关键注意点

  • 一定要处理state为NULL的情况:用SQL的COALESCE函数把null替换成类似'UNKNOWN'的默认值,避免Map出现null键导致序列化异常。
  • 不要用selectOne()处理多条结果的查询:如果查询返回多条记录,必须用selectList()或者配合@MapKey让MyBatis自动把List转成Map。

内容的提问来源于stack exchange,提问作者Robotyk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 11:39:58