首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 動態 SQL 構建與模糊查詢最佳實踐

     編輯:Java 相關知識

業務場景痛點

在開發通用查詢接口時,常面臨:用戶可能填寫任意組合的查詢條件(姓名、ID、電話、性別等),如何動態構建 SQL 既安全又高效?

核心解決方案:WHERE 1=1 + PreparedStatement

使用 WHERE 1=1 作為基礎條件,根據非空參數動態拼接 AND column = ?,配合 PreparedStatement 防止 SQL 注入。

完整實現代碼

import java.sql.*;
import java.util.*;
import org.apache.commons.lang3.StringUtils;

public class DynamicQueryDao {

    public List queryUsers(String name, String id, String tel, String sex) {
        List result = new ArrayList<>();
        StringBuilder sql = new StringBuilder("SELECT id, name, tel, sex FROM users WHERE 1=1");
        List params = new ArrayList<>();

        // 動態拼接條件
        if (StringUtils.isNotBlank(name)) {
            sql.append(" AND name LIKE ?");
            params.add("%" + name + "%");
        }
        if (StringUtils.isNotBlank(id)) {
            sql.append(" AND id LIKE ?");
            params.add("%" + id + "%");
        }
        if (StringUtils.isNotBlank(tel)) {
            sql.append(" AND tel LIKE ?");
            params.add("%" + tel + "%");
        }
        if (StringUtils.isNotBlank(sex)) {
            sql.append(" AND sex = ?");
            params.add(sex);
        }

        // 執行查詢
        try (Connection conn = DataSourceUtil.getConnection();
             PreparedStatement ps = conn.prepareStatement(sql.toString())) {

            // 綁定參數
            for (int i = 0; i < params.size(); i++) {
                ps.setObject(i + 1, params.get(i));
            }

            try (ResultSet rs = ps.executeQuery()) {
                while (rs.next()) {
                    User user = new User();
                    user.setId(rs.getString("id"));
                    user.setName(rs.getString("name"));
                    user.setTel(rs.getString("tel"));
                    user.setSex("1".equals(rs.getString("sex")) ? "男" : "女");
                    result.add(user);
                }
            }
        } catch (SQLException e) {
            throw new RuntimeException("查詢失敗", e);
        }
        return result;
    }
}


關鍵最佳實踐

1. 必須使用 PreparedStatement

  • 防止 SQL 注入(核心安全底線)
  • 預編譯提升重複查詢性能
  • 自動處理數據類型轉換與轉義

2. 參數收集用 List

避免手動管理索引錯誤,循環綁定時 i+1 對應 JDBC 1-based 索引。

3. 利用 Apache Commons Lang3

StringUtils.isNotBlank() 同時檢查 null、空字符串、空白字符,比原生 != null && !"".equals() 更健壯。

4. 分頁查詢擴展

// MySQL 分頁
sql.append(" LIMIT ?, ?");
params.add((pageNum - 1) * pageSize);
params.add(pageSize);

// Oracle 12c+ 分頁
sql.append(" OFFSET ? ROWS FETCH NEXT ? ROWS ONLY");

進階方案:QueryDSL / MyBatis 動態 SQL

對於複雜項目,建議遷移到成熟框架:

  • MyBatis:<where>、<if>、<choose> 標籤極其優雅
  • QueryDSL:類型安全的流式 API,編譯期檢查列名拼寫
  • JPA Specifications:Spring Data 規範,適合簡單動態查詢

MyBatis 映射器示例

<select id="selectUsers" resultType="User">
    SELECT id, name, tel, sex FROM users
    <where>
        <if test="name != null and name != ''">
            AND name LIKE CONCAT('%', #{name}, '%')
        </if>
        <if test="id != null and id != ''">
            AND id LIKE CONCAT('%', #{id}, '%')
        </if>
        <if test="tel != null and tel != ''">
            AND tel LIKE CONCAT('%', #{tel}, '%')
        </if>
        <if test="sex != null and sex != ''">
            AND sex = #{sex}
        </if>
    </where>
</select>

總結

  1. 動態查詢核心:WHERE 1=1 + 非空判斷拼接 + PreparedStatement
  2. 永遠不要拼接用戶輸入到 SQL 字符串中
  3. 簡單場景手寫 JDBC 即可,複雜場景上成熟 ORM 框架
  4. 注意 LIKE 查詢的通配符 % 在 Java 代碼中拼接,而非 SQL 中

掌握動態 SQL 構建模式,是從 CRUD 碼農進階為數據訪問層架構師的必經之路。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved