场景:做"操作日志 -> 获取操作人列表(姓名+职位)"接口时,控制台 MyBatis 日志能看到
position_name有值,但 Swagger 响应 JSON 里只有id/name,职位字段不见了。
1. 背景需求
操作日志页面有一个"操作人"筛选框/弹窗,需要后端提供一个人员列表接口,每条数据包含:
- 员工 id(staff.id)
- 员工姓名(staff.name)
- 员工职位名称(position.name)
涉及三张表的逻辑外键关系:
staff.id↔staff_orginfo.staff_idposition.id↔staff_orginfo.position_id
并且列表要按创建时间倒序(最新的在上)。
对应后端接口(示例):
GET /sys/optlog/operators?pageIndex=1&pageSize=10&name=管理员
2. 问题现象(非常迷惑)
调用接口后:
- 控制台 SQL 日志显示查询列里有
position_name,并且每行数据里职位也有值。 - 但 Swagger 的响应 JSON 里
rows只包含id、name,没有position_name。
也就是说:SQL 有值,但接口响应不展示该字段 。


3. 定位"问题在哪一层"
- SQL 层:数据库是否真的能查到字段?
- ORM 映射层:MyBatis 是否把列正确映射到 DTO 属性?
- JSON 序列化/Swagger 展示层:DTO 属性是否被序列化输出?
这次排查非常关键的一点是:
- 控制台既然能打印出
Columns: id, name, position_name,并且Row里职位也有值
→ 说明 SQL 层没问题。 - Swagger 响应里却没有职位字段
→ 极大概率是 ORM 映射层 或 序列化层 的问题。
4. 关键线索:MyBatis 下划线转驼峰映射
MyBatis(或 MyBatis-Plus)经常会开启一个全局配置:
mapUnderscoreToCamelCase=true
开启后,MyBatis 会把数据库列名自动做转换:
position_name→positionNameadd_time→addTime
这就带来一个"坑":
如果 DTO 里字段写成了下划线风格:
java
private String position_name;
而 SQL 返回的列名是 position_name,MyBatis 会尝试去找 positionName 来赋值,结果找不到,字段就一直是 null。
一旦 DTO 字段为 null,再结合项目常见的 JSON 序列化配置(不输出 null 字段),Swagger 里就会表现为:
"返回里根本没有这个字段"
这也是为什么明明 SQL 有值,Swagger 却像"没返回职位"。
5. 最终解决方案(两种任选其一)
方案 A(最简单):DTO 属性改成驼峰
把 DTO 的职位字段改为 positionName:
java
public class OptlogOperatorDTO {
private Long id;
private String name;
private String positionName;
}
然后 SQL 仍然用别名 position_name:
sql
SELECT p.name AS position_name
这样 MyBatis 会自动把 position_name 映射到 positionName,字段就能正常出现在 Swagger 响应里。
你这次最终采用的就是这个方案,修改后立刻生效。
方案 B(更稳健):XML 显式 resultMap 强制映射
如果你希望 DTO 里依然用下划线字段名(不推荐,但有时前端字段历史包袱很大),可以在 mapper.xml 写 resultMap:
xml
<resultMap id="OptlogOperatorDTOMap" type="com.xxx.OptlogOperatorDTO">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="position_name" property="position_name"/>
</resultMap>
<select id="selectOptlogOperators" resultMap="OptlogOperatorDTOMap">
SELECT s.id, s.name, p.name AS position_name
...
</select>
这样映射行为就不依赖全局配置,最稳定。
总结
这次问题之所以"看起来像 SQL/联表错了",是因为:
- SQL 的确查到了数据(控制台日志有值)
- 但 MyBatis 映射层因为 下划线转驼峰 的规则,把
position_name试图映射到positionName - DTO 字段名若写成
position_name,就会映射失败,字段变 null
最终通过统一 DTO 命名(positionName)或 resultMap 显式映射即可解决。