1. 动态SQL与模糊查询的黄金组合
作为一名常年与数据库打交道的开发者,我深刻理解查询功能在实际业务中的核心地位。而动态SQL实现模糊查询,可以说是数据处理领域的"瑞士军刀"——它既能应对复杂的查询需求,又能保持代码的优雅简洁。最近在优化一个客户管理系统时,我再次体会到这个技术组合的强大之处。
动态SQL允许我们根据运行时条件动态构建SQL语句,而模糊查询则突破了精确匹配的限制。当二者结合时,我们就能创建出灵活且强大的数据检索功能。比如在电商平台中搜索商品时,用户输入"手机"可能想找"智能手机"或"5G手机";在CRM系统中,输入"张"可能想查询所有张姓客户。这些都是模糊查询的典型应用场景。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心原理与技术选型
2.1 动态SQL的实现方式
动态SQL主要有三种实现路径:
- 字符串拼接:最基础的方式,通过条件判断拼接SQL字符串
java复制String sql = "SELECT * FROM products WHERE 1=1";
if (name != null) {
sql += " AND name LIKE '%" + name + "%'";
}
- 模板引擎:使用MyBatis、JPA等框架提供的动态SQL功能
xml复制<select id="searchProducts">
SELECT * FROM products
<where>
<if test="name != null">
AND name LIKE CONCAT('%', #{name}, '%')
</if>
</where>
</select>
- 查询构建器:如JOOQ、QueryDSL等类型安全的API
java复制List<Product> results = queryFactory.selectFrom(product)
.where(product.name.like("%" + name + "%"))
.fetch();
重要提示:字符串拼接方式存在SQL注入风险,在实际项目中应优先考虑后两种方案
2.2 模糊查询的语法差异
不同数据库对模糊查询的实现略有差异:
| 数据库 | LIKE语法 | 正则支持 | 性能特点 |
|---|---|---|---|
| MySQL | LIKE '%关键词%' |
REGEXP |
前导通配符会导致索引失效 |
| PostgreSQL | LIKE '%关键词%' |
~ 操作符 |
支持GIN索引加速 |
| Oracle | LIKE '%关键词%' |
REGEXP_LIKE |
函数索引可优化 |
| SQL Server | LIKE '%关键词%' |
PATINDEX |
包含索引可部分优化 |
3. 实战:构建安全的动态模糊查询
3.1 MyBatis动态SQL实现
以Spring Boot + MyBatis为例,实现一个多条件产品搜索接口:
java复制@Mapper
public interface ProductMapper {
List<Product> searchProducts(
@Param("name") String name,
@Param("category") String category,
@Param("minPrice") BigDecimal minPrice);
}
对应的XML映射文件:
xml复制<select id="searchProducts" resultType="Product">
SELECT * FROM products
<where>
<if test="name != null and name != ''">
AND name LIKE CONCAT('%', #{name}, '%')
</if>
<if test="category != null">
AND category = #{category}
</if>
<if test="minPrice != null">
AND price >= #{minPrice}
</if>
</where>
ORDER BY create_time DESC
</select>
3.2 JPA Criteria API实现
对于使用JPA的项目,可以通过Criteria API实现类型安全的动态查询:
java复制public List<Product> searchProducts(String name, String category, BigDecimal minPrice) {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Product> query = cb.createQuery(Product.class);
Root<Product> root = query.from(Product.class);
List<Predicate> predicates = new ArrayList<>();
if (StringUtils.isNotBlank(name)) {
predicates.add(cb.like(root.get("name"), "%" + name + "%"));
}
if (StringUtils.isNotBlank(category)) {
predicates.add(cb.equal(root.get("category"), category));
}
if (minPrice != null) {
predicates.add(cb.ge(root.get("price"), minPrice));
}
query.where(predicates.toArray(new Predicate[0]));
return entityManager.createQuery(query).getResultList();
}
4. 性能优化与避坑指南
4.1 索引使用策略
模糊查询最大的性能杀手是前导通配符(如%关键词)。优化方案包括:
- 后置通配符优先:
关键词%可以利用索引 - 全文索引:对于大文本字段,考虑使用FULLTEXT索引
- 搜索引擎集成:Elasticsearch等专业搜索工具
sql复制-- 创建支持后置通配符的索引
CREATE INDEX idx_product_name ON products(name(20));
-- 全文索引示例
CREATE FULLTEXT INDEX ft_idx_product_desc ON products(description);
4.2 参数预处理技巧
- 去除多余空格:
StringUtils.trim(input) - 限制输入长度:避免超长字符串导致性能问题
- 敏感字符转义:防止SQL注入
java复制public String safeLikeParam(String input) {
if (input == null) return null;
return "%" + input.trim()
.replace("!", "!!")
.replace("%", "!%")
.replace("_", "!_")
.replace("[", "