浅谈mybatis返回ResultMap,若是字段为空,返回值属性自动忽略问题。

探讨MyBatis在使用ResultMap时遇到的空值字段丢失问题,并提供解决方案,包括使用resultType替代及SQL层调整等方法。

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

前段时间,使用mybatis遇到一个奇葩问题。

当返回类型为ResultMap的时候,如果选择的字段为空,那么mybatis会忽略掉该对应属性。

也是就是,你想要这种类型的数据返回:

	{
		name : "隔壁老王",
		age : 20,
		sex : "男"
	}

然而,却返回这种数据:

	{
		name : "隔壁老王",
		age : 20
	}

没错,这是mybatis返回机制的锅。

讲道理,mybatis中不管你使用resultType还是resultMap,其实返回的结果都是结果集的Map。

唯一的不同,就是使用resultType的时候,mybatis会将Map结果集,赋值给resultType所对应的类。

也就是说,结果集合中,为空的字段也会进行赋值。

所以,当使用resultType的时候,是不会造成结果集空值丢失的情况。

那么很明显,解决方案中,就有将resultMap转换成实体类resultType的的方式。

即,根据需求,新建实体类来取代Map。

好吧,解决问题不难,但每次解决都那么繁琐,假设出现一个需求需要返回100个属性,那.......每次都要去新建一个类?

Are you kidding me ?

没错,我们可以尝试着从SQL层解决(mysql),比如使用函数,让选择字段不为空。

即,使用IFNULL(exp1, exp2)。当exp1为空的时候,返回值为expe2

<select id="findHasPowerForDirector" resultMap="sscaxsd">
    SELECT
    a.id, IFNULL(ifo.full_name, -1) AS full_name, IFNULL(sp.period_name, '空')
    AS period_name	, IFNULL(pt.thetime, '空') AS thetime
    FROM
    admin AS a
    LEFT JOIN admin_role AS ar
    ON a.role_id = ar.id
    LEFT JOIN admin_info AS ifo
    ON a.id = ifo.admin_id
    LEFT JOIN period_director_thetime AS pt
    ON a.id = pt.director_id
    LEFT JOIN school_period AS sp
    ON pt.period_id = sp.period_id
    WHERE
    a.admin_state != '0'
  </select>

最后,好像在配置文件中添加一行配置,也可以解决问题,但是,笔者没成功......emmmm




### MyBatis 返回 Map 类型结果集时跳过 Null 字段MyBatis 中,默认情况下,当查询返回 `Map` 类型的结果集时,即使某些字段的值为 `null`,这些字段仍然会作为键存在于 `Map` 中。然而,可以通过自定义方式实现忽略 `null` 值的功能。 #### 方法一:通过拦截器实现过滤功能 可以编写一个 MyBatis 插件来拦截查询结果并移除值为 `null` 的字段。以下是具体实现: ```java @Intercepts({ @Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class }) }) public class SkipNullFieldInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { Object result = invocation.proceed(); if (result instanceof List<?>) { ((List<Map<String, Object>>) result).forEach(map -> map.entrySet().removeIf(entry -> entry.getValue() == null)); } return result; } @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) {} } ``` 上述代码中,插件会在每次执行查询操作后检查返回值是否为列表形式,并逐一处理其中的每个 `Map` 对象,删除所有值为 `null` 的条目[^1]。 #### 方法二:手动设置 Mapper 查询方法 另一种更简单的方式是在业务层对返回的数据进行二次加工。例如,在 DAO 或 Service 层调用完成后立即清理掉不需要的字段: ```java private static Map<String, Object> removeNullFields(Map<String, Object> originalMap) { return originalMap.entrySet() .stream() .filter(entry -> entry.getValue() != null) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); } // 调用示例 Map<String, Object> resultMap = mapper.queryForMap(param); resultMap = removeNullFields(resultMap); ``` 这种方法虽然有效,但在性能上可能不如全局性的拦截器方案高效[^2]。 #### 方法三:调整 SQL 输出结构 还可以考虑修改 SQL 语句本身,使其仅选择那些非列。这通常适用于固定模式下的查询场景,但对于动态表单或者复杂联结查询来说不够灵活。 --- ### 总结 以上三种策略各有优劣: - **拦截器法**适合统一管理需求; - **手写工具函数**便于快速部署到现有项目中而无需额外依赖; - **SQL优化路径**则侧重于减少不必要的数据传输量。 最终的选择取决于实际应用场景以及团队的技术偏好。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值