使用setObject传递LocalTime批量更新时类型转换异常问题咨询
核心结论
该场景应当正常工作,但目前属于Connector/J的未修复BUG,在8.0.29、8.0.30、8.0.31版本中仍存在此问题。
问题原因
当同时启用rewriteBatchedStatements=true和useServerPrepStmts=true时,Connector/J的批量处理逻辑会进入ServerPreparedStatement的参数处理分支。该分支的代码(对应报错栈中的setOneBatchedParameterSet方法)未正确适配JDBC 4.2规范对java.time.LocalTime的支持,仍试图将LocalTime强制转换为java.sql.Time,从而触发ClassCastException。
而关闭其中任意一个参数时,批量处理会切换到兼容java.time类型的逻辑路径,因此不会出现异常。
临时解决方案
- 移除
useServerPrepStmts=true参数,放弃服务器端预编译优化,确保批量插入对LocalTime的支持 - 手动转换类型:将
LocalTime转为java.sql.Time后传入,修改代码如下:ps.setObject(1, java.sql.Time.valueOf(LocalTime.now()));
官方修复状态
截至Connector/J 8.0.31版本,官方更新日志中未包含该BUG的修复记录。此前LocalDate和LocalDateTime的类似问题已修复,但LocalTime属于遗漏场景,需等待官方后续版本修复。
问题复现代码
//MySQL 5.7 //Connector/J 8.0.28 String url = "jdbc:mysql://localhost:3306/dbname?rewriteBatchedStatements=true&useServerPrepStmts=true"; String sql = "INSERT INTO time_test(time) VALUES(?)"; try (Connection con = DriverManager.getConnection(url, "root", "root"); PreparedStatement ps = con.prepareStatement(sql) ) { con.setAutoCommit(false); for (int i = 0; i < 10; i++) { ps.setObject(1, LocalTime.now()); ps.addBatch(); } ps.executeBatch(); con.commit(); } catch (Exception e) { e.printStackTrace(); }
报错信息
java.lang.ClassCastException: class java.time.LocalTime cannot be cast to class java.sql.Time (java.time.LocalTime is in module java.base of loader 'bootstrap'; java.sql.Time is in module java.sql of loader 'platform')
at com.mysql.cj.jdbc.ServerPreparedStatement.setOneBatchedParameterSet(ServerPreparedStatement.java:759)
at com.mysql.cj.jdbc.ClientPreparedStatement.executeBatchedInserts(ClientPreparedStatement.java:712)
at com.mysql.cj.jdbc.ClientPreparedStatement.executeBatchInternal(ClientPreparedStatement.java:426)
at com.mysql.cj.jdbc.StatementImpl.executeBatch(StatementImpl.java:795)
at com.test.Test.main(Test.java:24)
内容的提问来源于stack exchange,提问作者Kevin

