Oracle19c下将含LIKE子句的SQL转为Java PreparedStatement遇异常求助
问题:Oracle 19c下LIKE子句转PreparedStatement后程序异常退出
问题背景
在Oracle 19c数据库环境中,把原有字符串拼接的LIKE子句SQL改成Java PreparedStatement后,程序执行时直接退出并返回主页面。
原有字符串拼接SQL代码
public int getCountReprintHistory(String request_date, String statement_type, String option_type, String cust_no) { String sql = ""; if ( request_date.equals("") && statement_type.equals("") && option_type.equals("") && cust_no.equals("") ) { sql += "SELECT COUNT(REPRINTLOG_DATE) FROM TBL_REPRINT_HISTORY2 "; sql += "ORDER BY REPRINTLOG_JOBID DESC "; } else { sql += "SELECT COUNT(REPRINTLOG_DATE) FROM TBL_REPRINT_HISTORY2 "; sql += "WHERE TO_CHAR(REPRINTLOG_DATE,'dd/mm/yyyy') LIKE '%" + request_date +"%' "; sql += "AND REPRINTLOG_STATEMENT_TYPE LIKE '%" + statement_type +"%' "; sql += "AND REPRINTLOG_OPTION_TYPE LIKE '%" + option_type +"%' "; sql += "AND REPRINTLOG_CUSTOMER_NO LIKE '%" + cust_no +"%' "; sql += "ORDER BY REPRINTLOG_JOBID DESC "; } return jdbcTemplate.queryForObject(sql, Integer.class); }
修改后的PreparedStatement代码(执行异常)
public int getCountReprintHistory(String request_date, String statement_type, String option_type, String cust_no) { String sql = ""; if ( request_date.equals("") && statement_type.equals("") && option_type.equals("") && cust_no.equals("") ) { sql += "SELECT COUNT(REPRINTLOG_DATE) FROM TBL_REPRINT_HISTORY2 "; sql += "ORDER BY REPRINTLOG_JOBID DESC "; } else { sql += "SELECT COUNT(REPRINTLOG_DATE) FROM TBL_REPRINT_HISTORY2 "; sql += "WHERE TO_CHAR(REPRINTLOG_DATE,'dd/mm/yyyy') LIKE '%' || ? || '%' "; sql += "AND REPRINTLOG_STATEMENT_TYPE LIKE '%' || ? || '%' "; sql += "AND REPRINTLOG_OPTION_TYPE LIKE '%' || ? || '%' "; sql += "AND REPRINTLOG_CUSTOMER_NO LIKE '%' || ? || '%' "; sql += "ORDER BY REPRINTLOG_JOBID DESC "; } return jdbcTemplate.queryForObject(sql, new Object[]{request_date,statement_type,option_type,cust_no}, Integer.class); }
问题分析与解决方法
核心问题
修改后的代码不管参数是否为空,都会把所有四个参数传入PreparedStatement。当某个参数是空字符串时,对应的条件会变成LIKE '%%'(匹配所有数据),这和原有逻辑冲突——原有逻辑只有当所有参数都为空时才不做过滤,否则所有参数都要参与过滤。另外,COUNT查询加ORDER BY完全多余,会增加数据库不必要的开销,甚至可能引发隐性异常。
修复方案
- 动态构建WHERE条件:只对非空参数添加对应的LIKE过滤,避免空参数导致的无效逻辑。
- 参数绑定优化:把通配符
%直接拼在参数值前后,而非在SQL中用||拼接,更符合PreparedStatement的使用规范。 - 移除无用排序:COUNT查询返回单一值,排序毫无意义,直接删除。
修复后代码
public int getCountReprintHistory(String request_date, String statement_type, String option_type, String cust_no) { StringBuilder sql = new StringBuilder("SELECT COUNT(REPRINTLOG_DATE) FROM TBL_REPRINT_HISTORY2"); List<Object> params = new ArrayList<>(); boolean hasCondition = false; // 处理日期参数 if (!request_date.isEmpty()) { sql.append(hasCondition ? " AND " : " WHERE "); sql.append("TO_CHAR(REPRINTLOG_DATE,'dd/mm/yyyy') LIKE ?"); params.add("%" + request_date + "%"); hasCondition = true; } // 处理单据类型参数 if (!statement_type.isEmpty()) { sql.append(hasCondition ? " AND " : " WHERE "); sql.append("REPRINTLOG_STATEMENT_TYPE LIKE ?"); params.add("%" + statement_type + "%"); hasCondition = true; } // 处理选项类型参数 if (!option_type.isEmpty()) { sql.append(hasCondition ? " AND " : " WHERE "); sql.append("REPRINTLOG_OPTION_TYPE LIKE ?"); params.add("%" + option_type + "%"); hasCondition = true; } // 处理客户编号参数 if (!cust_no.isEmpty()) { sql.append(hasCondition ? " AND " : " WHERE "); sql.append("REPRINTLOG_CUSTOMER_NO LIKE ?"); params.add("%" + cust_no + "%"); hasCondition = true; } // 根据参数数量执行对应查询 if (params.isEmpty()) { return jdbcTemplate.queryForObject(sql.toString(), Integer.class); } else { return jdbcTemplate.queryForObject(sql.toString(), params.toArray(), Integer.class); } }
额外优化建议
- 日期查询优化:不要用
TO_CHAR转换日期字段后再LIKE查询,建议把request_date转为Date类型,用REPRINTLOG_DATE BETWEEN ? AND ?做范围查询,这样可以利用日期索引,大幅提升查询效率。 - 空值判断优化:用Spring框架的
StringUtils.isEmpty()代替equals(""),可以同时处理null和空字符串的情况,更严谨。
内容的提问来源于stack exchange,提问作者iChinJO
相关产品推荐
相关产品推荐

