PAYATHON 2026

DB2 更新 VARCHAR 字段时报 NumberFormatException,如何排查?

数据小陈

结论

现有信息不足以确定根因,也不能据此认定 status 必须改成数字类型。日志显示 Hibernate 尝试以 VARCHAR 类型绑定字符串 PROCESSING,但 JDBC 调用返回了类型不匹配错误。

应先核对 SQL 和绑定日志:展示的更新语句只有一个 ?,异常却指向第 6 个参数。目前无法将这两段日志对应到同一条实际执行的 SQL。找到失败的完整语句及参数顺序后,才能判断哪个字段发生了转换。

PROCESSING 恰好是 10 个字符。仅从这个值和 varchar(10) 定义来看,没有证据表明字符串超长。

1. 将失败的 SQL 与绑定日志对应起来

已有日志包含:

binding parameter (6:VARCHAR) <- [PROCESSING]
Unable to bind parameter #6 - PROCESSING

展示的 SQL 则是:

UPDATE schema.table e
SET e.STATUS = ?

这条 SQL 只有一个参数位置,无法直接解释第 6 个参数为何绑定失败。需要从同一次任务执行中收集:

  • 完整 SQL 及其前后所有参数绑定日志。
  • 完整异常堆栈,尤其是最底层的 Caused by。
  • 实际调用的 Repository 方法及对应调用路径。
  • 是否启用了 JDBC batch,以及日志中是否混入其他操作。

如果使用 Spring Boot,且 Hibernate 日志类别与当前输出一致,可以增加以下配置:

logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.orm.jdbc.bind=TRACE

根据线程、执行时间和调用链关联日志,必要时在测试环境单独执行一次目标更新。参数日志可能包含业务数据,排查完成后应恢复合适的日志级别。

2. 核对应用实际连接的表和列

即使已经确认某张表的 STATUS 是 VARCHAR(10),也要确认应用更新的就是这个对象。

示例中出现了不同名称:

实体映射:test.Product
错误 SQL:schema.table
JPQL 实体名:Entity

如果这些只是脱敏名称,可以忽略差异;如果实际代码也是这样,就需要逐一核对。

使用与应用相同的数据库连接,确认数据库、schema、目标表或视图,以及 STATUS 的实际类型。如果失败的 SQL 有多个占位符,还要检查第 6 个参数对应的字段或表达式,不能只检查 STATUS。

同时检查该属性是否有额外的类型处理,例如 AttributeConverter、自定义 Hibernate 类型或其他影响读写的映射配置。当前实体片段没有展示这些配置,因此不能假定它们存在,也不能排除它们。

3. 修正示例中的映射和 JPQL 问题

@Query 默认按 JPQL 解析,应使用实体名和 Java 属性名。按照给出的 @Entity 声明,默认实体名是 Product;只有显式配置了 @Entity(name = "Entity"),才应使用 Entity。

原片段中的 @Table 两个属性之间还缺少逗号,应写为:

@Table(name = "Product", schema = "test")

缺少逗号是编译问题,不能解释已经发生的 JDBC 运行时异常。如果只是粘贴时遗漏,修正示例即可。

假设实际需求是更新指定产品,Repository 方法可以写成:

import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional;

@Modifying
@Transactional
@Query("UPDATE Product p SET p.status = :status WHERE p.id = :id")
int updateStatus(@Param("id") int id,
                 @Param("status") String status);

这个示例以默认实体名 Product 为前提,并返回受影响的行数。原语句没有 WHERE,会更新所有记录;如果确实需要全表更新,可以保留该行为。

@Modifying 用于标识修改查询,执行更新仍需要事务。如果 Service 层已经提供事务,可以由 Service 统一管理。事务配置和实体名需要正确,但它们是否与当前类型异常有关,仍需根据实际堆栈判断。

4. 用 JDBC 最小测试缩小范围

找到真正失败的 SQL 后,在测试环境使用相同的数据源、JDBC 驱动和参数,直接做一次 JDBC 测试。

如果已经确认目标是 test.Product,且只更新指定记录,核心绑定代码可以是:

String sql = "UPDATE test.Product SET STATUS = ? WHERE id = ?";

try (PreparedStatement statement = connection.prepareStatement(sql)) {
    statement.setString(1, "PROCESSING");
    statement.setInt(2, productId);
    int affectedRows = statement.executeUpdate();
}

这里假设 connection 和 productId 已由调用方提供,表名、列名与实际数据库一致。执行前应安排好测试数据及事务回滚,避免误改业务记录。

根据测试结果继续排查:

  • 如果在 setString 时就失败,重点检查驱动堆栈、实际 SQL 和参数位置。
  • 如果绑定成功、在 executeUpdate 时失败,继续检查数据库对象、约束、触发器及相关转换逻辑。
  • 如果直接 JDBC 成功,而 JPA 路径失败,优先比较两条路径的完整 SQL、全部参数、映射和执行方式。

如果直接 JDBC 也失败,再记录 JDBC 驱动名称、版本和 DB2 版本,根据具体组合核查兼容性或已知问题。当前没有这些版本信息,不能直接归因于驱动缺陷。

注意事项

不要通过截断 PROCESSING、改成数字或修改数据库列类型来试错。现有证据中最明确的不一致是单参数 SQL 与第 6 个参数绑定失败。先确认日志对应的实际执行语句,再沿着失败参数检查类型转换发生的位置。

备注:内容仅供参考。