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 个参数绑定失败。先确认日志对应的实际执行语句,再沿着失败参数检查类型转换发生的位置。
备注:内容仅供参考。
