关于使用mp时同时使用json处理器时出现java.lang.IllegalStateException: Failed to load ApplicationContext解决方法 Success
今天我在学习mybatis-plus的Json处理器的时候进行一个根据id查询用户简单测试,报出了问题根源如下机制特性具体表现全量校验任意SQL语句解析失败会导致整个Mapper初始化失败类型处理器继承限制手动编写SQL需显式指定类型处理器,无法继承实体类注解配置失败传播性单个SQL解析失败会导致同Mapper所有方法不可用使用json处理器时需要在sql的json字段中显式加typeHandle
相关类
mp相关接口、相关类均已正确配置,单元测试时使用了IService的getById方法
User类
package com.itheima.mp.domain.po;
import com.baomidou.mybatisplus.annotation.*;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import com.itheima.mp.enums.UserStatus;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import java.time.LocalDateTime;
@Data
@TableName(value = "user",autoResultMap = true)
public class User {
/**
* 用户id
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 用户名
*/
@TableField(value = "username")
private String username;
/**
* 密码
*/
private String password;
/**
* 注册手机号
*/
private String phone;
/**
* 详细信息
*/
@TableField(value = "info", typeHandler = JacksonTypeHandler.class)
private UserInfo info;
/**
* 使用状态(1正常 2冻结)
*/
private UserStatus status;
/**
* 账户余额
*/
private Integer balance;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 更新时间
*/
private LocalDateTime updateTime;
@TableLogic
private int deleted;
}
测试类
@Test
void test(){
long id = 1L;
User user = iuserService.getById(id);
System.out.println(user);
}
问题概述
今天我在学习mybatis-plus的Json处理器的时候进行一个根据id查询用户简单测试,报出了java.lang.IllegalStateException: Failed to load ApplicationContext
问题根源如下
Caused by: java.lang.IllegalStateException:
Type handler was null on parameter mapping for property 'info'.
It was either not specified and/or could not be found for the javaType (com.itheima.mp.domain.po.UserInfo) : jdbcType (null) combination.
说是无法找到typehandler映射给info,但我已经给info正确加上typehandler注解,mp理应会给我自动映射并转换。
解决思路
于是我查看我的xml文件,里面有一些手写sql
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mp.mapper.UserMapper">
<!-- 显示指定typeHandler-->
<insert id="saveUser" parameterType="com.itheima.mp.domain.po.User">
INSERT INTO `user` (`id`, `username`, `password`, `phone`, `info`, `balance`)
VALUES
(#{id}, #{username}, #{password}, #{phone}, #{info}, #{balance});
</insert>
<update id="updateUser" parameterType="com.itheima.mp.domain.po.User">
UPDATE `user`
<set>
<if test="username != null">
`username`=#{username}
</if>
<if test="password != null">
`password`=#{password}
</if>
<if test="phone != null">
`phone`=#{phone}
</if>
<if test="info != null">
`info`=#{info}
</if>
<if test="status != null">
`status`=#{status}
</if>
<if test="balance != null">
`balance`=#{balance}
</if>
</set>
WHERE `id`=#{id};
</update>
<delete id="deleteUser" parameterType="com.itheima.mp.domain.po.User">
DELETE FROM user WHERE id = #{id}
</delete>
<select id="queryUserById" resultType="com.itheima.mp.domain.po.User">
SELECT *
FROM user
WHERE id = #{id}
</select>
<select id="queryUserByIds" resultType="com.itheima.mp.domain.po.User">
SELECT *
FROM user
<if test="ids != null">
WHERE id IN
<foreach collection="ids" open="(" close=")" item="id" separator=",">
#{id}
</foreach>
</if>
LIMIT 10
</select>
<update id="customUpdate">
update user set balance = balance-#{amount} ${ew.customSqlSegment}
</update>
</mapper>
因为我是进行数据库查询的时候报出了错误,所以我以为是我xml文件中的select语句和IService
的方法产生了映射冲突,于是我把select语句都注释掉,但报错仍然没有解决。于是我把整个xml文件的sql语句都注释掉了,居然通过了!!
2025-04-15 17:24:44.495 INFO 20704 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2025-04-15 17:24:44.822 INFO 20704 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2025-04-15 17:24:44.830 DEBUG 20704 --- [ main] c.i.mp.mapper.UserMapper.selectById : ==> Preparing: SELECT id,username,password,phone,info,status,balance,create_time,update_time,deleted FROM user WHERE id=? AND deleted=0
2025-04-15 17:24:44.850 DEBUG 20704 --- [ main] c.i.mp.mapper.UserMapper.selectById : ==> Parameters: 1(Long)
2025-04-15 17:24:44.911 DEBUG 20704 --- [ main] c.i.mp.mapper.UserMapper.selectById : <== Total: 1
User(id=1, username=Jack, password=123456, phone=13234123521, info=UserInfo(age=20, intro=佛系青年, gender=male), status=Normal, balance=1300, createTime=2023-05-19T20:50:21, updateTime=2025-04-15T01:44:09, deleted=0)
但是我仍然很疑惑,不知道问题出现在哪,于是我每个语句都注释一遍看究竟是哪个sql产生了影响。
找到原因
<insert id="saveUser" parameterType="com.itheima.mp.domain.po.User">
INSERT INTO `user` (`id`, `username`, `password`, `phone`, `info`, `balance`)
VALUES
(#{id}, #{username}, #{password}, #{phone}, #{info}, #{balance});
</insert>
当我把这个sql注释掉后报错就没有了,原因如下:
- MyBatis Mapper XML解析机制
MyBatis在启动时会解析所有Mapper XML文件中的SQL语句。如果在解析过程中发现某个字段没有正确配置类型处理(TypeHandler),整个Mapper的初始化会失败,导致该Mapper中的所有方法都无法正常使用。 - Mapper初始化失败的影响
由于saveUser语句解析失败,整个UserMapper的初始化过程被中断,导致UserMapper中的所有方法(包括MyBatis-Plus自动生成的selectById方法)无法正确初始化。因此,在调用getById(id)时,虽然该方法本身配置了类型处理器,但由于Mapper初始化失败,类型处理器无法被正确应用,从而抛出类型处理器找不到的异常。
在sql中显式指定typeHandler类型即可解决
<insert id="saveUser" parameterType="com.itheima.mp.domain.po.User">
INSERT INTO `user` (`id`, `username`, `password`, `phone`, `info`, `balance`)
VALUES
(#{id}, #{username}, #{password}, #{phone}, #{info, typeHandler=com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler}, #{balance});
</insert>
但我发现我的一条update语句也包含对于info字段的处理,但是没有报错
<update id="updateUser" parameterType="com.itheima.mp.domain.po.User">
UPDATE `user`
<set>
<if test="username != null">
`username`=#{username}
</if>
<if test="password != null">
`password`=#{password}
</if>
<if test="phone != null">
`phone`=#{phone}
</if>
<if test="info != null">
`info`=#{info}
</if>
<if test="status != null">
`status`=#{status}
</if>
<if test="balance != null">
`balance`=#{balance}
</if>
</set>
WHERE `id`=#{id};
</update>
原因如下:
- 动态SQL的条件过滤
updateUser语句中使用了动态标签<if>,只有当info字段非空时,才会将该字段包含在更新的SQL语句中。如果实际业务中未修改info字段(即传入的User对象中info为null),则对应的<if>条件不成立,生成的SQL不会包含info字段,因此不会触发类型处理器需求。 - 更新与插入的机制差异
插入操作:必须处理所有非空字段的值,因此saveUser语句中的info字段必然被解析。
更新操作:只处理实际传入的非空字段。如果info未被修改,则不会出现在SQL中,避免了类型处理器的调用。运行时确保不报错还是需要手动显式加上typeHandler
总结
机制特性 | 具体表现 |
---|---|
全量校验 | 任意SQL语句解析失败会导致整个Mapper初始化失败 |
类型处理器继承限制 | 手动编写SQL需显式指定类型处理器,无法继承实体类注解配置 |
失败传播性 | 单个SQL解析失败会导致同Mapper所有方法不可用 |
使用json处理器时需要在sql的json字段中显式加typeHandler
更多推荐
所有评论(0)