Java操作SQLite插入NULL值时触发NumberFormatException求助
问题根源
你遇到的NumberFormatException核心原因很明确:待插入数据里的未开赛比分不是Java的null对象,而是字符串"null"!原来的判断 s[2] != null会返回true(因为这个字符串"null"本身是有效的Java对象,不是null),代码会强行把字符串"null"转成整数,自然触发异常。
另外还有个隐藏坑:就算你把score1/score2正确设为null,用stmt.setInt()也不行——setInt只能接受int基本类型,无法处理null值,会直接抛出NullPointerException,得用setObject来映射SQL的NULL。
修复方案
我给你一步步调整代码,解决这些问题:
1. 修改比分的判断逻辑
把字符串"null"(包括可能的空字符串、带空格的" null ")都视为null处理,代码改成:
Integer score1 = (s[2] == null || "null".equals(s[2].trim()) || s[2].trim().isEmpty()) ? null : Integer.parseInt(s[2].trim()); Integer score2 = (s[3] == null || "null".equals(s[3].trim()) || s[3].trim().isEmpty()) ? null : Integer.parseInt(s[3].trim());
加trim()是为了兼容数据里可能存在的空格,让逻辑更鲁棒。
2. 用setObject替代setInt处理null值
SQL的NULL需要通过JDBC的setObject方法设置,指定类型为Types.INTEGER,这样Java的null会被正确映射到SQLite的NULL:
stmt.setObject(3, score1, java.sql.Types.INTEGER); stmt.setObject(4, score2, java.sql.Types.INTEGER);
3. 修复winner计算的NPE风险
当赛事未开赛(dateValue == 0)时,score1和score2都是null,直接比较score1 > score2会抛NullPointerException,所以要先判断赛事状态再处理比分比较:
int winner; int dateValue = Integer.parseInt(s[4]); if (dateValue != 0) { // 已开赛的情况才比较比分,同时判空避免NPE if (score1 != null && score2 != null) { if (score1 > score2) { winner = 1; } else if (score2 > score1) { winner = 2; } else { winner = 0; // 平局 } } else { // 理论上已开赛不会有null比分,这里做兜底处理 winner = 0; } } else { winner = 3; // 未开赛 } stmt.setString(6, Integer.toString(winner)); // 你原来的代码漏了设置winner参数!
注意:原来的代码里没有把winner设置到PreparedStatement中,这会导致参数数量不匹配,我补上了这行关键代码。
完整修复后的代码
void insertFixtures(List<String[]> fixtures) throws SQLException{ String query = "REPLACE INTO games (team1_id, team2_id, score1, score2, created_at, winner) VALUES (? ,?, ?, ?, ?, ?)"; // 用try-with-resources自动关闭资源,避免资源泄漏 try (Connection con = DBConnector.connect(); PreparedStatement stmt = con.prepareStatement(query)) { for (String[] s : fixtures) { try{ stmt.setString(1, s[0]); stmt.setString(2, s[1]); // 处理比分:字符串"null"或空字符串都视为null Integer score1 = (s[2] == null || "null".equals(s[2].trim()) || s[2].trim().isEmpty()) ? null : Integer.parseInt(s[2].trim()); Integer score2 = (s[3] == null || "null".equals(s[3].trim()) || s[3].trim().isEmpty()) ? null : Integer.parseInt(s[3].trim()); // 用setObject设置null值,映射到SQL的NULL stmt.setObject(3, score1, java.sql.Types.INTEGER); stmt.setObject(4, score2, java.sql.Types.INTEGER); stmt.setString(5, s[4]); // 计算winner,避免空指针异常 int winner; int dateValue = Integer.parseInt(s[4]); if (dateValue != 0) { if (score1 != null && score2 != null) { if (score1 > score2) { winner = 1; } else if (score2 > score1) { winner = 2; } else { winner = 0; } } else { winner = 0; } } else { winner = 3; } stmt.setString(6, Integer.toString(winner)); stmt.addBatch(); // 用批量执行代替单次execute,提升插入效率 } catch (NumberFormatException e) { e.printStackTrace(); // 可以在这里添加错误日志,或者跳过无效数据 } } stmt.executeBatch(); // 统一执行批量插入 } }
额外优化点:用try-with-resources自动关闭连接和Statement,避免手动关闭可能遗漏的资源泄漏问题;改用addBatch()+executeBatch()批量执行插入,比循环里单次execute()效率高很多。
内容的提问来源于stack exchange,提问作者user11242688

