场景解决之mybatis当中非常用类型字段怎么映射

【MyBatis 填坑日记】IllegalStateException: Type handler was null on parameter mapping for property ‘options’

作者:@Neoest
时间:2025-07-29
关键词:MyBatis, TypeHandler, List, IllegalStateException


1. 问题现场

今天在联调一个新增接口时,后端突然抛出如下异常,整个服务直接 500:

Caused by: java.lang.IllegalStateException: 
Type handler was null on parameter mapping for property 'options'. 
It was either not specified and/or could not be found for the 
javaType (java.util.List) : jdbcType (null) combination.

异常栈截图:异常栈描述

2. 先定位代码

DAO 层的 Mapper 接口方法签名:

int insertQuestion(@Param("question") QuestionDTO question);

对应的 XML 片段:

<insert id="insertQuestion">
    INSERT INTO t_question(title, options, answer)
    VALUES (
        #{question.title},
        #{question.options},   <!-- 这里挂了 -->
        #{question.answer}
    )
</insert>

QuestionDTO 里 options 的定义:

private List<String> options;   // java.util.List

3. 为什么报错?

MyBatis 在解析 #{question.options} 时,发现:

  • javaType = java.util.List
  • jdbcType = null(你没显式写)
  • 没有合适的 TypeHandler 能把 List<String> 直接塞进一个 JDBC 列。

于是它只能抛出 IllegalStateException 告诉你:“臣妾做不到”。


4. 三种解决方案

✅ 方案 A:把 List 转成 JSON 字符串(推荐)

步骤 1:引入 Jackson / Fastjson 依赖

<!-- Jackson -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.15.2</version>
</dependency>

步骤 2:在 DTO 里加转换逻辑

@Data
public class QuestionDTO {
    private String title;

    @TableField(typeHandler = JacksonTypeHandler.class) // MyBatis-Plus 写法
    private List<String> options;

    private String answer;
}

步骤 3:XML 保持不动即可

#{question.options}   <!-- 现在 JacksonTypeHandler 会接管 -->

✅ 方案 B:手写 List → VARCHAR 的 TypeHandler

@MappedTypes(List.class)
@MappedJdbcTypes(JdbcType.VARCHAR)
public class ListStringTypeHandler extends BaseTypeHandler<List<String>> {

    private static final ObjectMapper MAPPER = new ObjectMapper();

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i,
                                    List<String> parameter, JdbcType jdbcType) throws SQLException {
        ps.setString(i, MAPPER.writeValueAsString(parameter));
    }

    @Override
    public List<String> getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return MAPPER.readValue(rs.getString(columnName), 
                              new TypeReference<List<String>>(){});
    }
    // 省略其余两个重载...
}

然后在 mybatis-config.xml 注册:

<typeHandlers>
    <typeHandler handler="com.xxx.ListStringTypeHandler"/>
</typeHandlers>

✅ 方案 C:直接改表结构 —— 拆表

如果业务上 options 是可变长度、需要查询元素,建议拆成子表:

t_question_option
-----------------
id | question_id | option_value

这样就不需要任何 TypeHandler,MyBatis 自动搞定。


5. 小结

方案适用场景侵入性备注
JSON + Jackson90% 场景最常用
自定义 TypeHandler需要精细控制代码多
拆表高频查询元素最规范

6. 一键复制版补丁

如果你只想快速修完跑路:

<!-- pom.xml -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.5</version>
</dependency>

<!-- DTO -->
@TableField(typeHandler = JacksonTypeHandler.class)
private List<String> options;

7. 参考资料

  • MyBatis 官方文档 – Type Handlers
  • Jackson 处理 JSON 数组最佳实践

如果本文帮到你,记得点赞收藏!评论区一起交流更多 MyBatis 坑位~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Neoest

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值