You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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完全多余,会增加数据库不必要的开销,甚至可能引发隐性异常。

修复方案

  1. 动态构建WHERE条件:只对非空参数添加对应的LIKE过滤,避免空参数导致的无效逻辑。
  2. 参数绑定优化:把通配符%直接拼在参数值前后,而非在SQL中用||拼接,更符合PreparedStatement的使用规范。
  3. 移除无用排序: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 16:22:54