我有一个使用 JDBC PreparedStatements 访问数据库的应用程序。为了保持数据访问的通用性,访问数据库的方法大量使用 PreparedStatement setObject 方法(请参阅讨论类似方法的问题)。一些伪代码来说明:
public ResultSet getResultSet(String sql, List<Object> parameters) throws SQLException {
PreparedStatement preparedStatement = db.getConnection().prepareStatement(sql);
for(int i = 0; i < parameters.size(); i++) {
preparedStatement.setObject(i+1, parameters.get(i));
}
return preparedStatement.executeQuery();
}
这减少了代码重复,因为 getResultSet(String, List) 可用于执行不同的查询。但是,它也有在运行时抛出 SQLExceptions 的风险。示例情况:
- 有人调用 getResultSet("SELECT columnWithVarchar2Type FROM someTable WHERE columnWithVarchar2Type = ?", Arrays.asList(someCustomObject));
- db.getConnection().prepareStatement(sql) 返回一个OraclePreparedStatement
- PreparedStatement.setObject 抛出 java.sql.SQLException '无效的列类型'
OraclePreparedStatement 的行为可能是合理的;它不知道如何将 someCustomObject 映射到 columnWithVarchar2Type。然而,对于 getResultSet(String, List) 的调用者来说,这是一个惊喜——毕竟,调用者提供了一个带有对象的列表,正如所要求的那样!
问题是:鉴于 PreparedStatement 实现之间的差异,有没有办法在 getResultSet 方法的签名中引入任何编译时安全性,以防止调用者传递将在运行时抛出 SQLExceptions 的参数?
完全的编译时安全当然是不可能的,但是有没有办法使用例如泛型来防止有人传入参数,而这些参数总是会导致 SQLException,而与传递的 SQL 无关?
编辑:这很可能甚至在理论上是不可能的(没有什么能阻止某人实现 PreparedStatement 并决定从不抛出 SQLExceptions 或总是从 setObject() 抛出 SQLExceptions)。但是,很高兴知道 ORM 是如何解决这个问题的。