【MyBatis】resultType踩坑实录

我忏悔我有罪,下次再也不偷懒了T_T

书接上文,我们已经新建了一个项目并写了俩简单的接口

一、恢复凶案现场

查询都已经正常返回结果,说明项目配置、Mapper映射以及基础查询流程都没有问题

但是,奇怪的事情出现了,,

我尝试为User表添加一个插入接口

我的xml:

我的Mapper:

我的service:

我的controller:(为了方便在浏览器中调用接口,这里统一都用@RequestMapping)看起来一片祥和,没啥问题,但是当我真正调用这个接口的时候,纷争开始了。。。

我们正常插入一条信息

使用navicat看一下

user表中正确插入了这条信息,欸,我们不是写了一个selectAll接口嘛,我们调这个接口来看一下

什么?!(教主同款哨音)

再看日志,

???,把张三塞哪去了?

二、真凶居然是它

我们先冷静一下,插入接口执行成功,数据库中确实插入了我们想要的数据,但是查询接口报错,问题不是SQL写错了,也不是数据库没数据,那就是MyBatis在把结果封装成对象这一步出现了问题

那么问题来了,MyBatis是怎么把查询结果封装成对象的?

回到User实体类,我们是这样写的

java 复制代码
@Data
public class User {

    private Integer id;
    private String userName;
    private Integer age;
    private Integer deptId;

    // 为了 insert 方便,偷了个懒
    public User(String userName, Integer age, Integer deptId) {
        this.userName = userName;
        this.age = age;
        this.deptId = deptId;
    }
}

当初的想法很简单,插入的时候直接new User(name, age, deptId),简单高效呀

没想到,正是这里爆雷了

由于User中存在带参构造方法,我们在XML中又使用了resultType,那么MyBatis会尝试使用构造方法进行映射

于是他开始这么干:

java 复制代码
new User( ? , ? , ? )

然后把查询结果按顺序往里塞

java 复制代码
id        -> 第一个参数
user_name -> 第二个参数
age       -> 第三个参数

很明显,这里传参的时候我没给id,第一个参数是userName,把这玩意往integer里面塞,不报错就怪了

三、如何死而复生?

坑已经出现了,该怎么填一下呢?

大致两种方法

3.1 缝缝补补又一年

很简单,MyBatis想用构造方法映射结果集,不让它用!!

我们补充一个无参的构造方法,让MyBatis老老实实走Setter注入,问题解决

补充一下这样做的原理:MyBatis在映射查询结果时,有一套固定的对象创建优先级策略

优先会尝试使用构造方法注入,实体类中存在可用的带参构造方法,我们在XML中使用的也是resultType,MyBatis会猜测构造方法参数,并与查询结果进行匹配。这也就是我们暴雷的那种方法,缺点很明显,这种匹配时隐式的,一旦顺序或者类型对不上,直接报错

找不到合适的构造方法,才会退回Setter注入,而Setter注入的前提是,类中必须存在一个无参构造方法

因此我们补无参构造,本质是在引导MyBatis走正确的路

3.2 引入新伙伴

既然User你完成不好这个操作,那我再定义一个类来帮你,顺便把你俩职责划分清晰一点

我们新定义一个类,UserDTO,专门用来接受参数

java 复制代码
public class User {

    private Integer id;
    private String name;
    private Integer age;
    private Integer deptId;

    public User() {
    }
    // getter / setter
}

它只关心select和resultMap,不关心插入时哪些字段必填

对应的,Mapper层也修改一下

java 复制代码
int insert(UserInsertDTO dto);

此时MyBatis的角色很纯粹:负责从DTO里取值,不再尝试"构造对象"

这样我们就不需要"凑一个User对象",而是直接创建一个待插入的用户

3.3 终极解决方案

前面的坑,本质原因其实一句话概括就是:MyBatis在使用resultType时,会自作聪明地推断如何创建对象

那我们不如直接告诉MyBatis,Java中的类对应数据表的那个字段

这种方式在表结构复杂、字段名和属性名对应不上、实体类有多个构造方法等场景下基本上是必选项

我们可以在xml文件中添加这样一段

xml 复制代码
<resultMap id="UserResultMap" type="com.example.demo2.entity.User">

    <id column="id" property="id"/>
    <result column="name" property="name"/>
    <result column="age" property="age"/>
    <result column="dept_id" property="deptId"/>

</resultMap>

使用resultMap

xml 复制代码
<select id="selectAll" resultMap="UserResultMap">
    select id, name, age, dept_id from user
</select>

这样MyBatis在处理查询结果的时候就完全不会再猜了

好啦,今天的坑就踩到这里吧

相关推荐
BD_Marathon4 小时前
MyBatis处理动态设置表名
mybatis
涵涵(互关)4 小时前
添加了 @TableId(type = IdType.AUTO) 但仍生成超大 ID
数据库·spring·mybatis
stillaliveQEJ5 小时前
【MyBatis】DML映射
java·mybatis
BD_Marathon5 小时前
搭建MyBatis框架之测试添加功能(六)
mybatis
麦兜*7 小时前
Spring Boot整合MyBatis-Plus实战:简化CRUD操作的最佳实践
spring boot·tomcat·mybatis
码界奇点8 小时前
基于Spring Boot与MyBatis-Plus的后台管理系统设计与实现
spring boot·后端·车载系统·毕业设计·mybatis·源代码管理
独断万古他化8 小时前
【MyBatis 深度解析】注解操作与 XML 配置:增删改查全流程实现
xml·java·spring·mybatis
码界奇点8 小时前
基于Spring+SpringMVC+MyBatis+easyUI的后台管理系统设计与实现
java·spring·毕业设计·mybatis·easyui·源代码管理
0和1的舞者8 小时前
《#{} vs ${}:MyBatis 里这俩符号,藏着性能与安全的 “生死局”》
java·数据库·学习·mybatis·intellij idea·mybatis操作