前言
在使用 mybatis 进行数据库查询时,你是否遇到过这样的情况:明明 sql 执行成功了,数据也查出来了,但返回的 java 对象中,某些字段却是 null?本文将从一个真实案例出发,深入剖析这个问题的根本原因,并给出三种解决方案,帮助你彻底搞懂 mybatis 的结果映射机制。
一、问题场景
1.1 数据库表结构
假设我们有一张汽车表 t_car,表结构如下:
create table t_car (
id bigint auto_increment primary key,
car_num varchar(255), -- 车牌号
brand varchar(255), -- 品牌
guide_price decimal(10,2), -- 指导价
produce_time char(10), -- 生产日期
car_type varchar(255) -- 车辆类型
);表中的数据如下:
| id | car_num | brand | guide_price | produce_time | car_type |
|---|---|---|---|---|---|
| 1 | 京a88888 | 宝驹535 | 20.00 | 2025-10-11 | 燃油车 |
| 2 | 京a66666 | 劳斯莱斯泡影 | 300.00 | 2025-10-12 | 新能源 |
| 3 | 海a8888 | 宝马 | 30.00 | 2023-02-02 | 燃油车 |
| ... | ... | ... | ... | ... | ... |
1.2 java 实体类
对应的 java 实体类 car.java:
public class car {
private long id;
private string carnum; // 对应数据库 car_num
private string brand;
private double guideprice; // 对应数据库 guide_price
private string producetime; // 对应数据库 produce_time
private string cartype; // 对应数据库 car_type
// getter / setter / tostring
}
1.3 mybatis mapper 配置
mapper 接口:
public interface carmapper {
list<car> selectall();
}
mapper xml 映射文件:
<select id="selectall" resulttype="car">
select * from t_car
</select>1.4 查询结果异常
执行查询后,得到的结果如下:
car{id=3, carnum='null', brand='宝马', guideprice=null, producetime='null', cartype='null'}
car{id=4, carnum='null', brand='宝马', guideprice=null, producetime='null', cartype='null'}
car{id=5, carnum='null', brand='宝马', guideprice=null, producetime='null', cartype='null'}
...问题:id 和 brand 正常映射,但 carnum、guideprice、producetime、cartype 均为 null。
这到底是为什么?
二、原因分析
2.1 根本原因:列名与属性名不匹配
观察表字段和 java 属性的对应关系:
| 数据库列名(下划线) | java 属性名(驼峰) | 是否一致 |
|---|---|---|
| id | id | ✅ 一致 |
| brand | brand | ✅ 一致 |
| car_num | carnum | ❌ 不一致 |
| guide_price | guideprice | ❌ 不一致 |
| produce_time | producetime | ❌ 不一致 |
| car_type | cartype | ❌ 不一致 |
在 mybatis 中,当使用 resulttype 进行自动映射时,默认要求 数据库列名与 java 属性名完全一致(大小写敏感)。
- 对于
id和brand,列名和属性名完全匹配 → ✅ 映射成功 - 对于
car_num等字段,列名和属性名不匹配 → ❌ 映射失败,属性保持null
这就是为什么部分字段能映射成功,而其他字段却为 null 的根本原因。
2.2 为什么没有报错?
mybatis 的自动映射是宽容的。对于无法匹配的列,它不会抛出异常,而是忽略该列的映射,java 对象中的对应属性会保持默认值(引用类型为 null,基本类型为默认值)。这就是为什么你能看到 carnum=null、guideprice=null 的结果。
三、解决方案(三选一)
方案一:开启驼峰命名自动转换 ⭐ 推荐
mybatis 提供了一个全局开关,可以将数据库的下划线命名自动映射为 java 的驼峰命名。
在 mybatis-config.xml 中添加:
<settings>
<setting name="mapunderscoretocamelcase" value="true"/>
</settings>开启后,mybatis 会自动完成以下映射:
car_num→carnumguide_price→guidepriceproduce_time→producetimecar_type→cartype
优点:
- 配置一次,全局生效。
- 所有 mapper 都不需要额外修改。
- 代码简洁,维护成本最低。
适用场景:所有项目都推荐开启,这是 mybatis 官方推荐的最佳实践之一。
方案二:使用resultmap手动映射
如果不想开启全局设置(例如项目中有特殊需求),可以使用 resultmap 手动指定列与属性的映射关系:
<resultmap id="carmap" type="car">
<id property="id" column="id"/>
<result property="carnum" column="car_num"/>
<result property="brand" column="brand"/>
<result property="guideprice" column="guide_price"/>
<result property="producetime" column="produce_time"/>
<result property="cartype" column="car_type"/>
</resultmap>
<select id="selectall" resultmap="carmap">
select * from t_car
</select>注意:使用 resultmap 时,<select> 标签中必须用 resultmap 属性,而不是 resulttype。
优点:
- 映射关系清晰可见。
- 支持复杂场景(一对一、一对多关联映射)。
缺点:
- 每个实体类都需要配置一个
resultmap,工作量较大。 - 字段较多时配置繁琐。
方案三:sql 中使用别名
在 sql 查询中直接为列起别名,让别名与 java 属性名一致:
<select id="selectall" resulttype="car">
select
id,
car_num as carnum,
brand,
guide_price as guideprice,
produce_time as producetime,
car_type as cartype
from t_car
</select>优点:
- 无需修改全局配置。
- 单个查询可独立控制映射。
缺点:
- 每个查询都需要写别名,工作量大。
- 容易遗漏,维护成本高。
- 违背 dry(don't repeat yourself)原则。
四、方案对比与选择建议
| 方案 | 配置成本 | 维护成本 | 适用场景 | 推荐度 |
|---|---|---|---|---|
| 开启驼峰转换 | 一次配置 | 低 | 所有项目 | ⭐⭐⭐⭐⭐ |
| resultmap 手动映射 | 每个实体一次 | 中 | 复杂映射、关联查询 | ⭐⭐⭐⭐ |
| sql 别名 | 每个查询一次 | 高 | 临时查询、特殊需求 | ⭐⭐ |
强烈建议:所有 mybatis 项目都应该开启 mapunderscoretocamelcase,这是最简单、最优雅的解决方案。
五、问题排查步骤
当遇到查询结果字段为 null 时,可以按以下步骤逐一排查:
第 1 步:检查实体类属性名与数据库列名是否一致
确认列名与属性名的对应关系,特别关注下划线命名和驼峰命名的差异。
第 2 步:检查是否开启了驼峰转换
在 mybatis-config.xml 中查看是否有以下配置:
<setting name="mapunderscoretocamelcase" value="true"/>
第 3 步:检查是否使用了resultmap且配置完整
如果使用了 resultmap,确认所有需要的列都已配置 <result> 或 <id>。
第 4 步:开启 mybatis 日志
在 mybatis-config.xml 中添加:
<setting name="logimpl" value="stdout_logging"/>
查看控制台输出的 sql 和参数,确认查询语句是否正确,列名是否符合预期。
第 5 步:检查 sql 是否 select 了所有需要的列
确认 sql 语句中包含了所有需要映射的列。如果 select 列表漏掉了某些列,它们在 java 对象中会保持默认值(null)。
六、深度扩展:为什么id和brand能正常映射?
在这个案例中,id 和 brand 正常映射,而其他字段却为 null,原因很直观:
| 数据库列名 | java 属性名 | 是否匹配 |
|---|---|---|
| id | id | ✅ 完全一致 |
| brand | brand | ✅ 完全一致 |
| car_num | carnum | ❌ 不一致(有下划线) |
mybatis 的自动映射是基于名称的。完全匹配的能映射,不完全匹配的(如包含下划线)在不开启驼峰转换的情况下,无法映射。
七、最佳实践总结
- 统一命名规范:数据库使用下划线命名,java 使用驼峰命名,这是业界标准做法。
- 始终开启驼峰转换:在 mybatis-config.xml 中配置 mapunderscoretocamelcase=true,这是 mybatis 的标配。
- 复杂映射使用 resultmap:当关联查询(一对一、一对多)或需要自定义映射规则时,使用 resultmap。
- 开启日志调试:开发环境下开启 stdout_logging,直观查看 sql 执行情况。
- 单元测试验证:对 mapper 方法编写单元测试,确保映射逻辑正确。
八、完整配置示例
最终的 mybatis-config.xml:
<?xml version="1.0" encoding="utf-8" ?>
<!doctype configuration
public "-//mybatis.org//dtd config 3.0//en"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<!-- 开启驼峰命名转换(核心) -->
<setting name="mapunderscoretocamelcase" value="true"/>
<!-- 开启日志 -->
<setting name="logimpl" value="stdout_logging"/>
</settings>
<typealiases>
<package name="com.xie.entity"/>
</typealiases>
<environments default="development">
<environment id="development">
<transactionmanager type="jdbc"/>
<datasource type="pooled">
<property name="driver" value="com.mysql.cj.jdbc.driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</datasource>
</environment>
</environments>
<mappers>
<package name="com.xie.mapper"/>
</mappers>
</configuration>开启驼峰转换后,查询结果将正常显示:
car{id=3, carnum='海a8888', brand='宝马', guideprice=30.0, producetime='2023-02-02', cartype='燃油车'}
car{id=4, carnum='海a8888', brand='宝马', guideprice=30.0, producetime='2023-02-02', cartype='燃油车'}
...结语
mybatis 查询结果字段为 null 的问题,90% 以上的情况都是由于列名与属性名不匹配造成的。通过开启 mapunderscoretocamelcase 这个开关,你可以一劳永逸地解决下划线命名与驼峰命名的映射问题,让数据访问层的代码更加简洁、可靠。
到此这篇关于mybatis 查询结果字段为null的三种解决方案的文章就介绍到这了,更多相关mybatis 查询结果字段为null内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论