首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> MySQL 資料庫 >> MySQL 綜合教學

動態組裝 SQL 查詢:拒絕 1=1 陷阱、WhereClauseBuilder 最佳實踐

     編輯:MySQL 綜合教學

動態組裝 SQL 查詢:拒絕 1=1 陷阱、WhereClauseBuilder 最佳實踐

多條件搜尋介面常需動態生成 WHERE 子句。濫用 WHERE 1=1 導致全表掃描、索引失效。本文對比錯誤與正確模式,給出 WhereClauseBuilder 設計模式參考實作。

❌ 反模式:WHERE 1=1 陷阱

String sql = "SELECT * FROM T_Employee WHERE 1=1";
if (empNoChecked) sql += " AND FNumber BETWEEN '"+v1+"' AND '"+v2+"'";
if (nameChecked)  sql += " AND FName LIKE '%"+name+"%'";
if (ageChecked)   sql += " AND FAge BETWEEN "+a1+" AND "+a2;
executeSQL(sql);

危害:
1. 優化器識別 1=1 為常數真,無法利用索引範圍掃描 → 全表掃描。
2. 字串拼接極易引發 SQL 注入。
3. 複雜條件下 SQL 可讀性差、難除錯。

✅ 正規模式:WhereClauseBuilder

public class WhereClauseBuilder {
    private final StringBuilder sql = new StringBuilder();
    private boolean hasWhere = false;

    public WhereClauseBuilder(String baseSql) {
        sql.append(baseSql);  // e.g. "SELECT * FROM T_Employee"
    }

    public WhereClauseBuilder addCondition(String condition) {
        if (condition == null || condition.trim().isEmpty()) return this;
        if (!hasWhere) {
            sql.append(" WHERE ").append(condition);
            hasWhere = true;
        } else {
            sql.append(" AND ").append(condition);
        }
        return this;
    }

    public String build() { return sql.toString(); }
}

// 使用範例
WhereClauseBuilder builder = new WhereClauseBuilder("SELECT * FROM T_Employee");
if (empNoChecked) builder.addCondition("FNumber BETWEEN ? AND ?");
if (nameChecked)  builder.addCondition("FName LIKE ?");
if (ageChecked)   builder.addCondition("FAge BETWEEN ? AND ?");

String finalSql = builder.build();
// 使用 PreparedStatement 綁定參數,防注入、利用執行計劃緩存
PreparedStatement ps = conn.prepareStatement(finalSql);
int idx = 1;
if (empNoChecked) { ps.setString(idx++, v1); ps.setString(idx++, v2); }
if (nameChecked)  { ps.setString(idx++, "%"+name+"%"); }
if (ageChecked)   { ps.setInt(idx++, a1); ps.setInt(idx++, a2); }
ResultSet rs = ps.executeQuery();

核心優勢

維度WHERE 1=1WhereClauseBuilder
索引利用❌ 失效✅ 正常範圍/Ref 掃描
SQL 注入高風險✅ PreparedStatement 參數化
執行計劃緩存不穩定✅ 固定 SQL 模板可復用
可維護性差✅ 條件邏輯集中、易擴展

進階:MyBatis / JPA Criteria / QueryDSL

  • MyBatis:<where> 標籤自動處理 AND/OR 前綴,<if test="..."> 動態拼接。
  • JPA Criteria API:型別安全的物件化查詢構建。
  • QueryDSL:流暢 API、編譯期檢查、支援多種後端。

避坑清單

  • ❌ 任何形式的 WHERE 1=1 / WHERE 1=1 AND ...。
  • ❌ 字串拼接參數值 → SQL 注入 + 計劃不復用。
  • ✅ 永遠用 PreparedStatement / ORM 參數綁定。
  • ✅ 動態條件構建器集中管理 WHERE 邏輯。
  • ✅ 複雜報表查詢考慮物化視圖/匯總表預聚合。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved