mybatis之HashMap返回为null时,key不保存导致得到[null]

本文解决了一个在MyBatis中查询结果为[null]的问题,通过调整配置参数returnInstanceForEmptyRow和callSettersOnNulls,使得查询结果为空列表而非包含null的列表。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

写这么多只是为了记录自己此次解决问题的过程和个中原理,可能会比较啰嗦,若不想看,拉到文章最下面直接看解决方案。

在进行ibatis升级至mybatis中,遇到一个问题

List<Map> list = this.getSqlMap().queryForList("mer.queryForTrans",sendmap);  

list的返回结果为[null],size=1。这个肯定是有问题的,我在数据库中执行了这条SQL,确实是没有记录。但返回结果应该是list=[],size=0 。这个问题困扰了我几天,期间也在网上查了很多资料,很多人说因为mybatis中HashMap得到null时,是不保存key的。我觉得这好像说的很有道理啊,然后就按照网上说的添加了<setting name="callSettersOnNulls" value="true" /> 。这个是在查询某列返回结果为null时,仍然保存该key,而key的值为null。可为什么这在别人那好使我这就不起作用,但是网上基本上能找到的都是这个办法。只能靠我自己了,今天上班接着跟这个问题杠上,我非得解决出。从这条语句一路debug进去,花了近半个小时查看mybatis对该SQL语句的执行情况,重点看mybatis的jar包是怎么对数据库返回的空记录保存并赋值的,发现了问题所在。

问题就在这个rowValue原本是我们期待的结果{a=null,b=null,c=null},foundValues=false,但在执行了401行语句后rowValue变成null值,所以是这个configuration.isReturnInstanceForEmptyRow() 返回了false,导致rowValue变成了null,也就有了所谓的令人费解的[null]。进入这个configuration类,看到这是一个boolean类型的变量,看到这个变量名字我便认定一定和这个变量有关!


在网上查了之后是这么说的 

returnInstanceForEmptyRow:MyBatis, by default, returns null when all the columns of a returned row are NULL. When this setting is enabled, MyBatis returns an empty instance instead. Note that it is also applied to nested results (i.e. collectioin and association). Since: 3.4.2。原来mybatis中默认当返回的记录每一列全都为null值时,便默认返回一个null的实例。所以我在项目的mybatis配置文件中又添加了<setting name="returnInstanceForEmptyRow" value="true" />。最终问题顺利解决。

在mybatis的配置文件中以下两个setting一定要搭配使用。mybatis版本要在3.4.2以上。


### 将 MyBatis 返回的 Map 中的 Key 转为大写 在 MyBatis 中,默认返回的 `Map` 类型结果是基于数据库查询字段名作为键值的,这些键值通常是小写的(具体取决于数据库的字段命名规则)。如果需要将返回的 `Map` 的键转为大写,可以通过自定义结果处理器来实现。以下是实现方法: #### 方法:使用 `ResultSetHandler` 自定义处理逻辑 MyBatis 提供了对 `ResultSetHandler` 的扩展支持,允许开发者自定义结果集的处理逻辑。通过实现 `org.apache.ibatis.executor.resultset.DefaultResultSetHandler` 或直接在 Mapper 方法中使用 `@ResultMap` 和 `@MapKey` 注解,可以实现对返回结果的修改。 以下是一个完整的代码示例: ```java import org.apache.ibatis.executor.resultset.DefaultResultSetHandler; import org.apache.ibatis.session.SqlSession; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.util.HashMap; import java.util.Map; public class UppercaseKeyResultSetHandler extends DefaultResultSetHandler { @Override protected Map<String, Object> handleRow(ResultSet rs) throws Exception { Map<String, Object> resultMap = new HashMap<>(); ResultSetMetaData metaData = rs.getMetaData(); int columnCount = metaData.getColumnCount(); for (int i = 1; i <= columnCount; i++) { String columnName = metaData.getColumnName(i).toUpperCase(); // 将列名转为大写 Object value = rs.getObject(i); resultMap.put(columnName, value); } return resultMap; } public static void main(String[] args) { try (SqlSession session = MyBatisUtil.getSqlSessionFactory().openSession()) { UppercaseKeyResultSetHandler handler = new UppercaseKeyResultSetHandler(); // 使用自定义的 ResultSetHandler 处理查询结果 session.select("yourMapperId", null, handler); } } } ``` 上述代码通过继承 `DefaultResultSetHandler` 并重写 `handleRow` 方法,实现了对每一行数据的键值转换逻辑[^4]。 #### 方法:使用拦截器(Interceptor) 另一种方式是通过 MyBatis 的拦截器机制,在结果返回之前对 `Map` 的键进行修改。以下是一个拦截器的实现示例: ```java import org.apache.ibatis.executor.resultset.DefaultResultSetHandler; import org.apache.ibatis.plugin.*; import java.sql.ResultSet; import java.util.Map; import java.util.Properties; @Intercepts({ @Signature(type = DefaultResultSetHandler.class, method = "handleResultSets", args = {ResultSet.class}) }) public class UppercaseKeyInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { Object result = invocation.proceed(); if (result instanceof Map) { Map<String, Object> resultMap = (Map<String, Object>) result; Map<String, Object> upperCaseMap = new HashMap<>(); resultMap.forEach((key, value) -> upperCaseMap.put(key.toUpperCase(), value)); return upperCaseMap; } return result; } @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) { } } ``` 在 MyBatis 配置文件中注册该拦截器即可生效: ```xml <plugins> <plugin interceptor="com.example.UppercaseKeyInterceptor"/> </plugins> ``` #### 注意事项 - 如果查询结果包含嵌套结构(如 `List<Map>`),需要递归处理每个 `Map`。 - 拦截器方式适用于全局场景,而自定义 `ResultSetHandler` 更适合特定查询的需求[^5]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值