我的程序将一个简单的 CSV 加载到表中。文件中的数据应与表格的布局匹配。
(简化的)代码是:
String sql = "select * from " + _tableName +
" where 1 < 0";
PreparedStatement stmt = _CONN.prepareStatement(sql);
ResultSet rs = stmt.executeQuery();
ResultSetMetaData rsmd = rs.getMetaData();
rs.close();
stmt.close();
_columnCount = rsmd.getColumnCount();
sql = "insert into " + _tableName + " values (" +
"?,".repeat(_columnCount - 1) + "?)";
stmt = _CONN.prepareStatement(sql);
...
int line_no = 0;
readingInput:
for (String line : Files.readAllLines(input,
StandardCharsets.UTF_8)) {
line_no++;
String[] sa = line.split(_inputSeperator, -1);
int column = 0;
for (String st : sa) try {
column++; /* JDBC numbers columns from 1... */
stmt.setObject(column, st,
rsmd.getColumnType(column));
} catch(SQLException e) {
logger.error("{}:{} SQL Exception parsing field {} of <<{}>>",
_fileName, line_no, column, line);
continue readingInput;
}
stmt.addBatch();
}
...
stmt.executeBatch();
实际上,这适用于有效输入,但我的一个测试尝试了一条伪造的行——尝试将字符串插入数字列。
此类无效输入确实会产生错误,但仅在最后的
stmt.executeBatch()
行处产生错误,而我对此没有做好准备:java.sql.BatchUpdateException: Error converting data type nvarchar to int.
当无效值传递到
Statement.setObject()
时,为什么错误不会触发——我的代码准备报告它,跳过虚假输入行并继续?
所有的PreparedStatment.setFoo()方法都被声明为抛出
SQLException
——为什么setObject()
实际上不抛出一个?
也许这样做是为了节省时间——并且有一些选项可以立即验证传递给PreparedStatement的值,即使需要更长的时间?
该行为是特定于驱动程序的。一些 JDBC 驱动程序将自行验证转换(因为它们在客户端应用转换,因为服务器需要特定类型),然后您将在调用
setXXX
时遇到异常。其他 JDBC 驱动程序将简单地使用您在 set
上使用的类型的 SQL 等效项将类型化值发送到服务器,并且在执行时,服务器将执行所需的转换,并且如果请求的转换要么成功,要么失败不可能或未满足某些要求。
由于数据库服务器中的功能和行为之间存在巨大差异,因此 JDBC 规范允许两者兼而有之。您无法要求在特定点应用转换。