项目实战
项目实战
John DoeSpringBoot MyBatis 使用
Created: 8/21/2025 23:39
Updated: 9/1/2025 23:22
Exported: 9/1/2025 23:28
Link: https://chatgpt.com/c/68a73d70-355c-8330-a12b-671f82c5cf39
Prompt:springboot使用mybatis时什么时候要加resultType
Response:
在 Spring Boot + MyBatis 开发中,resultType 一般是用在 Mapper XML 的 <select> 语句中,作用是告诉 MyBatis 查询结果要映射成什么 Java 类型。是否要加 resultType,取决于你查询的返回结果和返回类型的对应关系。
我帮你总结下常见场景:
✅ 需要加 resultType 的情况
- 返回简单类型(基本类型或包装类、String)→ 因为返回的是单个数值,必须指定
1
2
3<select id="countUsers" resultType="int">
SELECT COUNT(*) FROM user
</select>resultType。 - 返回 Map(键值对)→ 每行结果会映射成一个
1
2
3<select id="getUserAsMap" resultType="map">
SELECT id, name FROM user WHERE id = #{id}
</select>Map。 - 返回 POJO,但没有写
resultMap,只靠字段名映射→ MyBatis 会自动根据列名和属性名对应关系封装。1
2
3<select id="getUserById" resultType="com.example.demo.User">
SELECT id, name, age FROM user WHERE id = #{id}
</select>
🚫 不需要加 resultType 的情况
- 你在 XML 中使用了
<resultMap>→ 已经显式用1
2
3
4
5
6
7
8<resultMap id="UserMap" type="com.example.demo.User">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="age" property="age"/>
</resultMap>
<select id="getUserById" resultMap="UserMap">
SELECT id, name, age FROM user WHERE id = #{id}
</select>resultMap定义映射规则,就不用resultType。 - 你用的是注解方式(@Select 等)并且返回类型通过方法签名已确定→ 方法返回值是
1
2
User getUserById(int id);User,不需要resultType。
💡 规则总结
- 简单结果(基本类型、字符串、Map、POJO,且字段名和属性名一致):用
resultType。 - 复杂映射(字段名和属性名不一致、多表联合查询、需要定制映射):用
resultMap。 - 注解方式:一般不用
resultType,MyBatis 会根据返回值类型自动映射。
要不要我帮你画一个 “什么时候用 resultType / resultMap” 的判断流程图,让你一眼就能看出来?
Prompt:
实体中有一个status属性班级状态,显示为:未开班、已结课、在读中 这三种。
如果:
- 当前时间 > 结课时间:状态未 已结课。
- 当前时间 < 开课时间:状态未 未开班。
- 否则,就是 在读中。但是表中没有这个字段要怎么办
Response:
你这个场景其实就是 数据库表里没有存储 status 字段,而是需要根据开课时间、结课时间动态计算。这种需求很常见,做法主要有两类:
✅ 方案一:实体类里加 非持久化字段(推荐)
在实体 Class 里定义一个 status 字段,但标记为不映射数据库列:
1 |
|
然后在 Service 层 或 Mapper 查询结果处理 时计算:
1 | public void fillStatus(ClassEntity clazz) { |
查询列表时可以统一处理:
1 | List<ClassEntity> list = classMapper.selectList(null); |
✅ 方案二:SQL 里直接计算(如果想少写 Java 逻辑)
在 Mapper XML 里用 CASE WHEN 计算 status 并返回:
1 | <select id="selectClassList" resultType="com.example.demo.ClassEntity"> |
这样查询出来的实体 status 就有值了,前端直接显示即可。
⚠️ 注意:这种写法需要 ClassEntity 里有 status 字段(但数据库表里可以没有,因为 SQL 已经返回了这个别名字段)。
✅ 方案三:视图(不常用)
如果业务大量依赖这个 status,也可以在数据库里建一个 视图 view,视图里包含计算好的 status 字段,Mapper 查询视图即可。
📌 总结
- 如果只是在业务层用到,推荐方案一(Java 层计算,灵活且数据库无侵入)。
- 如果查询出来就要直接显示,推荐方案二(SQL 里用 CASE WHEN 计算,返回 status 别名字段)。
- 如果是全局需求,方案三(视图)也可考虑。
Prompt:CASE WHEN常见使用场景和使用方法
Response:
好问题 👍,CASE WHEN 在 SQL 中是非常常见的条件判断语句,相当于 SQL 里的 if-else。
我帮你总结下 常见使用场景 和 具体用法,同时给你一些例子。
🔹 CASE WHEN 的基本语法
1 | CASE |
👉 说明:
WHEN ... THEN ...:条件成立返回对应结果ELSE:所有条件都不满足时的返回值(可省略,省略则返回NULL)END:结束
🔹 常见使用场景
1. 字段值映射(字典/枚举翻译)
比如数据库里存储的是数字,前端需要显示文字:
1 | SELECT |
👉 相当于枚举值翻译。
2. 动态分组统计
统计不同区间的人数:
1 | SELECT |
👉 按年龄段分组统计人数。
3. 条件聚合(在 SELECT 里做分类统计)
1 | SELECT |
👉 相当于把一列数据拆成多列统计。
4. 条件排序
根据不同条件自定义排序规则:
1 | SELECT id, name, status |
👉 控制排序优先级,而不是按字典序。
5. 时间状态判断(很常用)
课程状态计算:
1 | SELECT |
👉 类似你刚才问的场景。
6. 避免 NULL(默认值替代)
1 | SELECT |
👉 对 NULL 做友好处理。
🔹 小技巧
- 可以嵌套使用:
1
2
3
4CASE WHEN a > 10
THEN CASE WHEN b > 5 THEN 'X' ELSE 'Y' END
ELSE 'Z'
END - 可用于 WHERE 子句(但不推荐,逻辑容易乱):
1
2
3
4
5
6SELECT * FROM orders
WHERE
CASE
WHEN status = '已付款' THEN 1
ELSE 0
END = 1; - 最好别滥用:简单的判断推荐交给 Java 业务层处理,SQL 里主要用在 分组、聚合、统计、展示翻译 这些场景。
Prompt:
为什么 when now()<c.begin_date then ‘未开班’报错Element c.begin_date is not allowed here:
4/4
Response:
我帮你分析下这个问题 👇
报错信息:
1 | Element c.begin_date is not allowed here |
这是 MyBatis 在解析 XML 的时候 报出来的,并不是 SQL 本身的错误。
原因是:MyBatis 的 XML 标签里,< 和 > 会被 XML 解析器当作标签符号,导致解析失败。
在你的 SQL 里有:
1 | when now()<c.begin_date then '未开班' |
XML 解析器看到 <c.begin_date 以后,会以为是个新的标签 <c...>,于是报错。
✅ 解决办法:用转义字符
在 MyBatis 的 XML 里,所有比较符号要写成转义形式:
<→<>→>
所以你的 SQL 改成这样就对了:
1 | <select id="list" resultType="com.example.pojo.Clazz"> |
🔹 补充说明
这是 MyBatis 里非常常见的坑:
- 写在
<select>、<if>、<where>等 XML 标签里的 SQL,都会先经过 XML 解析器解析,所以<>必须写成转义符。 - 如果你用的是 注解方式(
@Select),就不需要转义,直接写<>就行,因为注解不是 XML。
Prompt:自定义异常 + 全局异常处理器实现错误信息提示
Response:
在 Spring Boot 项目里,实现「自定义异常 + 全局异常处理器」是一个非常常见的做法,用来统一管理错误提示。
我给你一个完整的实现思路和代码示例:
🔹 1. 定义自定义异常类
通常我们会定义一个 业务异常,继承 RuntimeException,携带错误码和错误信息。
1 | public class BusinessException extends RuntimeException { |
🔹 2. 定义统一返回结果类
方便前端收到一致的 JSON 格式。
1 | import lombok.Data; |
🔹 3. 定义全局异常处理器
用 @RestControllerAdvice + @ExceptionHandler 来捕获异常,返回统一的 JSON。
1 | import org.springframework.web.bind.annotation.ExceptionHandler; |
🔹 4. 使用示例
在 Service 或 Controller 里,你可以直接抛出 BusinessException:
1 |
|
在 Controller 里调用:
1 |
|
前端收到的 JSON 错误信息:
1 | { |
🔹 5. 总结
- 自定义异常:携带业务错误信息
- 全局异常处理器:统一拦截,返回 JSON
- 统一返回格式:前端不用关心后端具体抛的什么异常,始终能拿到
code + message
Prompt:
org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException:
Error updating database. Cause: java.lang.IllegalArgumentException: invalid comparison: java.time.LocalDate and java.lang.String
The error may exist in com/example/mapper/StudentMapper.xml
The error may involve com.example.mapper.StudentMapper.updateById
The error occurred while executing an update
Cause: java.lang.IllegalArgumentException: invalid comparison: java.time.LocalDate and java.lang.String
2/2
Response:
这个报错关键信息是:
1 | invalid comparison: java.time.LocalDate and java.lang.String |
👉 说明 MyBatis 在执行 update 时,把 LocalDate 类型和数据库中的 String 类型对比/赋值时不匹配。
🔍 常见原因
- 数据库字段类型和实体字段类型不匹配
- 实体类用的是
LocalDate - 数据库列可能是
varchar/char/text(字符串类型),而不是date或datetime
⚠️ MyBatis 在执行参数设置时,发现LocalDate无法自动转换为String,就报错了。
- 实体类用的是
- SQL 写法导致的隐式比较
比如 XML 里写了类似:如果1
2
3<if test="beginDate != null">
and begin_date = #{beginDate}
</if>begin_date在数据库是字符串类型,而beginDate是LocalDate,就会触发LocalDate vs String的比较错误。
- MyBatis 类型处理器没生效
MyBatis 默认对java.time.LocalDate有支持,但前提是数据库字段类型要是DATE。
如果数据库字段是VARCHAR,它不知道怎么把LocalDate自动转成字符串。
✅ 解决办法
方案一:改数据库字段类型(推荐 ✅)
- 如果字段确实表示日期,数据库字段改为
DATE或DATETIME。 - 这样 MyBatis + JDBC 就能自动把
LocalDate映射到DATE了。
1 | ALTER TABLE student MODIFY begin_date DATE; |
方案二:在实体类改字段类型
- 如果数据库就是
VARCHAR存储日期,那实体类里字段也改成String。 - 逻辑层需要转换时自己用
LocalDate.parse(str)。
1 | private String beginDate; // 实体类保持和数据库一致 |
方案三:自定义 TypeHandler(不改表结构也不改实体)
如果你希望数据库用 VARCHAR 存日期,但 Java 里用 LocalDate,可以写一个自定义 TypeHandler:
1 |
|
然后在 application.yml 里注册:
1 | mybatis: |
📌 总结
- 最根本的原因:Java 字段和数据库字段类型不一致(LocalDate vs String)
- 最佳做法:改数据库字段为 DATE/DATETIME,和 LocalDate 保持一致
- 如果不能改表结构,就用 TypeHandler 或 实体字段用 String 来解决。
Prompt:总结
Response:
当然可以 👍,我帮你梳理一下你这几轮问答里学到的知识点:
- MyBatis 映射
resultType用于简单结果映射(基本类型、POJO、Map),而resultMap适合复杂映射(字段名和属性名不一致、多表查询)。- 在 XML 中写
<、>要用转义<、>,否则会被解析成标签。
- CASE WHEN 的使用
- 可以做字段值翻译(枚举映射)、动态分组、条件聚合、条件排序、时间状态判断、避免 NULL。
- 写法相当于 SQL 里的
if-else。
- 动态状态计算
- 如果表里没有
status字段,可以在 Java 实体类加@TableField(exist = false)非持久化字段,在 Service 层计算; - 或者在 SQL 中用
CASE WHEN直接生成status。
- 如果表里没有
- 异常处理
- 定义自定义异常类(带错误码和信息),结合全局异常处理器(
@RestControllerAdvice + @ExceptionHandler)统一返回 JSON。 - 这样前端能拿到统一格式的错误提示。
- 定义自定义异常类(带错误码和信息),结合全局异常处理器(
- 类型映射问题
- MyBatis 报
LocalDate vs String错误,原因是 Java 字段和数据库字段类型不一致。 - 解决方案:
- 改数据库字段为
DATE/DATETIME; - 或实体字段改为
String; - 或写自定义
TypeHandler处理LocalDate与VARCHAR的转换。
- 改数据库字段为
- MyBatis 报
👉 简单一句话总结:
你学会了 MyBatis 查询结果映射的正确用法、SQL 里 CASE WHEN 的典型应用、如何处理表中不存在的计算字段、Spring Boot 全局异常处理的实现,以及 MyBatis 与 Java/数据库类型不匹配时的解决思路。


