MyBatis实现动态SQL模糊查询的示例代码

 更新时间:2026年02月02日 09:06:40   作者:鹿角片ljp  
在数据库查询中,模糊查询是最常用的功能之一,今天我们来探讨如何通过动态SQL实现灵活、安全的模糊查询,文中的示例代码讲解详细,有需要的小伙伴可以了解下

在数据库查询中,模糊查询是最常用的功能之一。然而,当查询条件变得复杂多变时,静态SQL往往显得力不从心。今天我们来探讨如何通过动态SQL实现灵活、安全的模糊查询。

一、为什么需要动态SQL模糊查询

1.1 传统模糊查询的局限性

-- 静态SQL示例
SELECT * FROM users WHERE username LIKE '%张%' 
AND email LIKE '%example.com%';

这种写法的问题在于:

  • 当某个条件为空时,查询会失效
  • 条件组合多变时,需要写大量重复代码
  • 难以应对复杂的业务逻辑

1.2 动态SQL的优势

  • 灵活性:根据实际参数动态生成SQL
  • 可维护性:代码更简洁,易于维护
  • 性能优化:避免不必要的查询条件

二、MyBatis动态SQL实现模糊查询

2.1 基础示例:单条件模糊查询

<!-- MyBatis Mapper XML -->
<select id="searchUsers" resultType="User">
    SELECT * FROM users
    <where>
        <if test="keyword != null and keyword != ''">
            AND (username LIKE CONCAT('%', #{keyword}, '%')
            OR email LIKE CONCAT('%', #{keyword}, '%')
            OR phone LIKE CONCAT('%', #{keyword}, '%'))
        </if>
    </where>
</select>

2.2 多条件组合模糊查询

<select id="advancedSearch" resultType="User">
    SELECT * FROM users
    <where>
        <!-- 姓名模糊查询 -->
        <if test="name != null and name != ''">
            AND username LIKE CONCAT('%', #{name}, '%')
        </if>
        
        <!-- 邮箱模糊查询 -->
        <if test="email != null and email != ''">
            AND email LIKE CONCAT('%', #{email}, '%')
        </if>
        
        <!-- 电话号码模糊查询(支持中间四位*号) -->
        <if test="phonePattern != null and phonePattern != ''">
            AND phone LIKE REPLACE(#{phonePattern}, '*', '%')
        </if>
        
        <!-- 地址多字段模糊查询 -->
        <if test="address != null and address != ''">
            AND (
                province LIKE CONCAT('%', #{address}, '%')
                OR city LIKE CONCAT('%', #{address}, '%')
                OR detail LIKE CONCAT('%', #{address}, '%')
            )
        </if>
    </where>
    ORDER BY create_time DESC
</select>

2.3 使用<choose>实现条件选择

<select id="smartSearch" resultType="User">
    SELECT * FROM users
    <where>
        <choose>
            <when test="searchType == 'name' and keyword != null">
                AND username LIKE CONCAT('%', #{keyword}, '%')
            </when>
            <when test="searchType == 'email' and keyword != null">
                AND email LIKE CONCAT('%', #{keyword}, '%')
            </when>
            <when test="searchType == 'phone' and keyword != null">
                AND phone LIKE CONCAT('%', #{keyword}, '%')
            </when>
            <otherwise>
                AND status = 'ACTIVE'
            </otherwise>
        </choose>
    </where>
</select>

三、Java代码中的动态构建

3.1 使用StringBuilder动态构建SQL

// 服务层代码示例
public List<User> dynamicSearch(UserSearchCriteria criteria) {
    StringBuilder sql = new StringBuilder("SELECT * FROM users WHERE 1=1");
    List<Object> params = new ArrayList<>();
    
    // 姓名模糊查询
    if (StringUtils.isNotBlank(criteria.getName())) {
        sql.append(" AND username LIKE ?");
        params.add("%" + criteria.getName() + "%");
    }
    
    // 邮箱模糊查询
    if (StringUtils.isNotBlank(criteria.getEmail())) {
        sql.append(" AND email LIKE ?");
        params.add("%" + criteria.getEmail() + "%");
    }
    
    // 分页处理
    if (criteria.getPageSize() > 0) {
        sql.append(" LIMIT ?, ?");
        params.add(criteria.getOffset());
        params.add(criteria.getPageSize());
    }
    
    return jdbcTemplate.query(sql.toString(), params.toArray(), 
        new BeanPropertyRowMapper<>(User.class));
}

3.2 使用JPA Specification实现(Spring Data JPA)

// 使用Specification构建动态查询
public class UserSpecifications {
    
    public static Specification<User> nameContains(String name) {
        return (root, query, cb) -> 
            StringUtils.isBlank(name) ? 
            cb.conjunction() : 
            cb.like(root.get("username"), "%" + name + "%");
    }
    
    public static Specification<User> emailContains(String email) {
        return (root, query, cb) -> 
            StringUtils.isBlank(email) ? 
            cb.conjunction() : 
            cb.like(root.get("email"), "%" + email + "%");
    }
    
    public static Specification<User> multiFieldSearch(String keyword) {
        return (root, query, cb) -> {
            if (StringUtils.isBlank(keyword)) {
                return cb.conjunction();
            }
            String pattern = "%" + keyword + "%";
            return cb.or(
                cb.like(root.get("username"), pattern),
                cb.like(root.get("email"), pattern),
                cb.like(root.get("phone"), pattern)
            );
        };
    }
}

// 使用示例
public List<User> searchUsers(String name, String email) {
    return userRepository.findAll(
        Specification.where(UserSpecifications.nameContains(name))
            .and(UserSpecifications.emailContains(email))
    );
}

四、高级技巧与优化

4.1 防止SQL注入

// 使用预编译语句,永远不要直接拼接用户输入
String safePattern = "%" + escapeSql(keyword) + "%";

// MyBatis自动处理参数,防止SQL注入
<if test="keyword != null">
    AND username LIKE CONCAT('%', #{keyword}, '%')
</if>

4.2 性能优化建议

-- 为经常查询的字段创建索引
CREATE INDEX idx_username ON users(username);
CREATE INDEX idx_email ON users(email);

-- 避免前导通配符导致索引失效的情况
-- 不推荐:LIKE '%keyword%' 
-- 推荐:LIKE 'keyword%'(如果业务允许)

4.3 使用全文索引提升模糊查询性能

-- MySQL全文索引示例
ALTER TABLE users ADD FULLTEXT INDEX ft_search (username, email);

-- 使用全文索引进行模糊查询
SELECT * FROM users 
WHERE MATCH(username, email) AGAINST('+张* +example*' IN BOOLEAN MODE);

五、实际应用场景

5.1 电商商品搜索

<select id="searchProducts" resultType="Product">
    SELECT * FROM products
    <where>
        <if test="productName != null">
            AND product_name LIKE CONCAT('%', #{productName}, '%')
        </if>
        <if test="categoryId != null">
            AND category_id = #{categoryId}
        </if>
        <if test="minPrice != null">
            AND price >= #{minPrice}
        </if>
        <if test="maxPrice != null">
            AND price <= #{maxPrice}
        </if>
        <!-- 模糊搜索商品描述 -->
        <if test="keyword != null">
            AND (
                product_name LIKE CONCAT('%', #{keyword}, '%')
                OR description LIKE CONCAT('%', #{keyword}, '%')
                OR tags LIKE CONCAT('%', #{keyword}, '%')
            )
        </if>
    </where>
    ORDER BY 
    <choose>
        <when test="sortBy == 'price'">price ${sortOrder}</when>
        <when test="sortBy == 'sales'">sales_count DESC</when>
        <otherwise>create_time DESC</otherwise>
    </choose>
</select>

5.2 日志查询系统

public List<Log> searchLogs(LogQuery query) {
    StringBuilder sql = new StringBuilder(
        "SELECT * FROM system_logs WHERE 1=1");
    
    // 模糊匹配操作内容
    if (StringUtils.isNotBlank(query.getContent())) {
        sql.append(" AND content LIKE ?");
        params.add("%" + query.getContent() + "%");
    }
    
    // 模糊匹配用户IP
    if (StringUtils.isNotBlank(query.getIp())) {
        sql.append(" AND ip_address LIKE ?");
        params.add(query.getIp() + "%");  // IP前缀匹配
    }
    
    // 时间范围查询
    if (query.getStartTime() != null) {
        sql.append(" AND create_time >= ?");
        params.add(query.getStartTime());
    }
    
    return jdbcTemplate.query(sql.toString(), 
        params.toArray(), 
        new BeanPropertyRowMapper<>(Log.class));
}

六、最佳实践总结

安全性第一:始终使用参数化查询,防止SQL注入

性能优化:为频繁查询的字段建立索引,考虑使用全文搜索

代码可读性:保持SQL语句的清晰和可维护性

适度使用:避免过度复杂的动态SQL,必要时拆分查询

测试覆盖:确保各种条件组合都能正确工作

结语

动态SQL模糊查询是现代应用开发中不可或缺的技能。通过合理运用MyBatis动态标签、JPA Specification或自定义SQL构建,我们可以在保证安全性的同时,实现灵活高效的查询功能。记住,好的查询设计不仅能让程序跑得更快,也能让代码更易于维护和扩展。

到此这篇关于MyBatis实现动态SQL模糊查询的示例代码的文章就介绍到这了,更多相关MyBatis SQL模糊查询内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 探讨:使用httpClient在客户端与服务器端传输对象参数的详解

    探讨:使用httpClient在客户端与服务器端传输对象参数的详解

    本篇文章是对使用httpClient在客户端与服务器端传输对象参数进行了详细的分析介绍,需要的朋友参考下
    2013-06-06
  • Java8 Instant 时间戳实例讲解

    Java8 Instant 时间戳实例讲解

    Instant类是Java8 中补充的一个 时间戳类,nstant 可以使用静态方法 now()或者of()方法来创建一个实例对象,本文通过实例代码讲解Java8 Instant 时间戳,感兴趣的朋友跟随小编一起看看吧
    2022-11-11
  • Java实现的最大匹配分词算法详解

    Java实现的最大匹配分词算法详解

    这篇文章主要介绍了Java实现的最大匹配分词算法,简单说明了最大匹配分词算法的原理并结合具体实例形式最大匹配分词算法的实现方法与相关注意事项,需要的朋友可以参考下
    2017-09-09
  • 一文教会你如何从0到1搭建一个SpringBoot项目

    一文教会你如何从0到1搭建一个SpringBoot项目

    今天刚好学习到SpringBoot,就顺便记录一下吧,下面这篇文章主要给大家介绍了关于如何从0到1搭建一个SpringBoot项目的相关资料,文中通过图文介绍的非常详细,需要的朋友可以参考下
    2024-01-01
  • 面试JAVA时,问到spring该怎么回答

    面试JAVA时,问到spring该怎么回答

    这篇文章主要介绍了Spring面试资料,学Java的小伙伴都知道Spring是面试的必问环节,看完了一天就可掌握数据结构和算法的面试题,快来看看吧
    2021-08-08
  • Spring Boot源码实现StopWatch优雅统计耗时

    Spring Boot源码实现StopWatch优雅统计耗时

    这篇文章主要为大家介绍了Spring Boot源码实现StopWatch优雅统计耗时,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-07-07
  • SpringBoot 内置 CollectionUtil用法实战指南

    SpringBoot 内置 CollectionUtil用法实战指南

    文章介绍了SpringBoot中自带的CollectionUtil类,包括判空、转换、合并、匹配和兜底功能,并通过实战例子详细展示了这些功能的应用场景和优势,感兴趣的朋友跟随小编一起看看吧
    2025-12-12
  • 使用Maven和SpringBoot搭建客户数据清洗项目框架

    使用Maven和SpringBoot搭建客户数据清洗项目框架

    这篇文章主要为大家详细介绍了如何使用Maven和SpringBoot搭建客户数据清洗项目框架,文中的示例代码讲解详细,感兴趣的小伙伴可以了解一下
    2025-07-07
  • springcloud教程之zuul路由网关的实现

    springcloud教程之zuul路由网关的实现

    这篇文章主要介绍了springcloud教程之zuul路由网关的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-02-02
  • java接口返回参数按照请求参数进行排序方式

    java接口返回参数按照请求参数进行排序方式

    这篇文章主要介绍了java接口返回参数按照请求参数进行排序方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09

最新评论