假设我有查询:
SELECT * FROM CUSTOMERS WHERE CUSTOMER_ID = ?
使用PreparedStatement,我可以绑定变量:
pstmt.setString(1, custID);
但是,我无法通过以下绑定获得正确的结果:
pstmt.setString(1, null);
结果是:
SELECT * FROM CUSTOMERS WHERE CUSTOMER_ID = NULL
这不会产生任何结果。正确的查询应为:
SELECT * FROM CUSTOMERS WHERE CUSTOMER_ID IS NULL
常见的解决方案是:
动态生成查询:
String sql = "SELECT * FROM CUSTOMERS WHERE CUSTOMER_ID " + (custID==null ? "IS NULL" : "= ?"); if (custID!=null) pstmt.setString(1, custID);
使用NVL转换空值的乱码值:
NVL
SELECT * FROM CUSTOMERS WHERE NVL(CUSTOMER_ID, 'GIBBERISH') = NVL(?, 'GIBBERISH');
但是您需要100%确保永远不会存储值’GIBBERISH’。
有没有一种方法可以使用静态查询 并 避免依赖乱码的值转换?我正在寻找类似的东西:
SELECT * FROM CUSTOMERS WHERE /** IF ? IS NULL THEN CUSTOMER_ID IS NULL ELSE CUSTOMER_ID = ? **/
我想我可能有一个可行的解决方案:
SELECT * FROM CUSTOMERS WHERE ((? IS NULL AND CUSTOMER_ID IS NULL) OR CUSTOMER_ID = ?) pstmt.setString(1, custID); pstmt.setString(2, custID);
以上工作可靠吗? 有没有更好的方法(可能只需要设置一次参数)?还是根本没有办法可靠地做到这一点?
您的工作解决方案很好(并且与我之前使用的类似)。如果只想绑定一次,则可以使用CTE或内联视图为实际查询提供值:
WITH CTE AS ( SELECT ? AS REAL_VALUE FROM DUAL ) SELECT C.* -- but not * really, list all the columns FROM CTE JOIN CUSTOMERS C ON (CTE.REAL_VALUE IS NULL AND C.CUSTOMER_ID IS NULL) OR C.CUSTOMER_ID = CTE.REAL_VALUE
因此,只有一个占位符可以绑定。
不过,我真的没有看到Java分支的问题,除非您的实际查询复杂得多,并且会导致大量重复。