理解 MyBatis JdbcType

背景

有时候,在使用 MyBatis 时会报下面的错误:

Error setting null for parameter #6 with JdbcType OTHER . Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. Cause: java.sql.SQLException: 无效的列类型:1111
; uncategorized SQLException for SQL []; SQL state [99999]; error code [17004]; 无效的列类型:1111; nested exception is java.sql.SQLException: 无效的列类型:1111

错误通常发生在传给 Insert 或 Update 语句的参数为 null 的时候。比如:

<insert id="insertCustomerLog" parameterType="map">
insert into customer_log
(
CUSTOMER_SERVICE_USER_NAME,
user_name,
CONTENT,
LOG_FIRST_TYPE,
STATUS,
LINKED_ID,
FEE,
ACCOUNT_FIRST_TYPE,
ACCOUNT_SECOND_TYPE,
ACCOUNT_THIRD_TYPE,
LOG_SECOND_TYPE,
LOG_IP,
MEMO
)
values
(
#{customerServiceUserName},
#{username},
#{content},
#{logFirstType},
#{status},
#{linkedId},
#{fee},
#{accountFirstType},
#{accountSecondType},
#{accountThirdType},
#{logSecondType},
#{logIp},
#{memo}
)
</insert>

#{logIp}等其他参数为 null 时,就会报上面的错误。通常修复的方式是明确指定#{logIp}等参数的 JdbcType:

<insert id="insertCustomerLog1" parameterType="com.diyicai.customer.domain.CustomerLog">
insert into customer_log
(
CUSTOMER_SERVICE_USER_NAME,
user_name,
CONTENT,
LOG_FIRST_TYPE,
STATUS,
LINKED_ID,
FEE,
ACCOUNT_FIRST_TYPE,
ACCOUNT_SECOND_TYPE,
ACCOUNT_THIRD_TYPE,
LOG_SECOND_TYPE,
LOG_IP,
MEMO
)
values
(
#{customerServiceUserName,jdbcType=VARCHAR},
#{username,jdbcType=VARCHAR},
#{content,jdbcType=VARCHAR},
#{logFirstType,jdbcType=NUMERIC},
#{status,jdbcType=NUMERIC},
#{linkedId,jdbcType=VARCHAR},
#{fee,jdbcType=NUMERIC},
#{accountFirstType,jdbcType=NUMERIC},
#{accountSecondType,jdbcType=NUMERIC},
#{accountThirdType,jdbcType=NUMERIC},
#{logSecondType,jdbcType=NUMERIC},
#{logIp,jdbcType=VARCHAR},
#{memo,jdbcType=VARCHAR}
)
</insert>

分析

MyBatis 底层,由 TypeHandler 的 setParameter 方法将 Mapper 方法参数中的值赋值给 PreparedStatement,不同的类型由不同的 TypeHandler 实现处理,比如 IntegerTypeHandler、StringTypeHandler 等,StringTypeHandler 等常用 TypeHandler 都继承自 BaseTypeHandler,BaseTypeHandler 中实现了 setParameter 方法:

@Override
public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
if (parameter == null) {
if (jdbcType == null) {
throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");
}
try {
ps.setNull(i, jdbcType.TYPE_CODE);
} catch (SQLException e) {
throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . " +
"Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. " +
"Cause: " + e, e);
}
} else {
try {
setNonNullParameter(ps, i, parameter, jdbcType);
} catch (Exception e) {
throw new TypeException("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType + " . " +
"Try setting a different JdbcType for this parameter or a different configuration property. " +
"Cause: " + e, e);
}
}
}

可以看到这是一个模板方法,对 setParameter 参数 parameter 为 null 的情况做了统一处理,子类只需要重写 setNonNullParameter 方法即可。而前面提到的报错就是在ps.setNull(i, jdbcType.TYPE_CODE)报错时抛出的。

之所以ps.setNull(i, jdbcType.TYPE_CODE)报错,是因为大多数数据库驱动在设置 null 时,需要指定 JdbcType 的类型。有的驱动需要 JdbcType 和列的类型一致,而有的驱动需要设置为指定类型比如 NULL、VARCHAR 或 OTHER。MyBatis 默认使用的是 OTHER,而数据库驱动不支持,所以报错。

MyBatis 支持 JdbcTypeForNull 配置,用来指定当参数为 null 时,设置 JdbcType 的类型,比如对于 Oracle 10g,可将其设置为 NULL:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="jdbcTypeForNull" value="NULL" />
</settings>
</configuration>

参考:

mybatis 需要注意的点 MyBatis 插入空值时,需要指定 JdbcType

MyBatis 的 jdbcType 的作用

MyBatis 3 | Configuration – mybatis

java - MyBatis - jdbcTypeForNull Oracle

MyBatis-Error setting null parameter

posted @   Higurashi-kagome  阅读(70)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
历史上的今天:
2022-12-26 MySQL日期加减
2022-12-26 el-tree全部展开全部折叠方法
2021-12-26 Markdown 文件中的 Emoji 变成了问号
点击右上角即可分享
微信分享提示