logo

MyBatis进阶技巧:模糊查询与IN条件的高效实现

作者:新兰2025.09.19 16:32浏览量:0

简介:本文深入探讨MyBatis框架中模糊查询与IN条件查询的实现方法,从基础语法到性能优化,提供可落地的技术方案。

MyBatis进阶技巧:模糊查询与IN条件的高效实现

一、模糊查询的核心实现方式

1.1 基础LIKE语法实现

MyBatis中实现模糊查询最直接的方式是通过XML映射文件中的LIKE语句。在SQL语句中,通常需要结合#{}${}参数占位符使用:

  1. <!-- 方式1:前端拼接通配符 -->
  2. <select id="searchByName" resultType="User">
  3. SELECT * FROM user
  4. WHERE name LIKE CONCAT('%', #{keyword}, '%')
  5. </select>
  6. <!-- 方式2:后端拼接通配符(需注意SQL注入) -->
  7. <select id="searchByNameUnsafe" resultType="User">
  8. SELECT * FROM user
  9. WHERE name LIKE '%${keyword}%'
  10. </select>

关键区别#{}会进行预编译处理,有效防止SQL注入;而${}直接拼接字符串,存在安全风险。建议优先使用#{}配合数据库函数实现。

1.2 动态SQL的灵活应用

对于多条件模糊查询场景,MyBatis的动态SQL标签能显著提升代码可维护性:

  1. <select id="advancedSearch" resultType="User">
  2. SELECT * FROM user
  3. <where>
  4. <if test="name != null">
  5. AND name LIKE CONCAT('%', #{name}, '%')
  6. </if>
  7. <if test="email != null">
  8. AND email LIKE CONCAT('%', #{email}, '%')
  9. </if>
  10. </where>
  11. </select>

最佳实践:使用<where>标签自动处理AND/OR前缀,避免手动拼接时出现的语法错误。

1.3 注解方式实现

对于简单场景,可通过@Select注解直接编写SQL:

  1. @Select("SELECT * FROM user WHERE name LIKE CONCAT('%', #{keyword}, '%')")
  2. List<User> searchByName(@Param("keyword") String keyword);

适用场景:单表简单查询,复杂场景仍建议使用XML配置。

二、IN条件查询的深度解析

2.1 静态IN列表处理

当IN条件值固定时,可直接在XML中编写:

  1. <select id="getUsersByIds" resultType="User">
  2. SELECT * FROM user
  3. WHERE id IN (1, 2, 3, 5, 8)
  4. </select>

2.2 动态IN列表实现

实际开发中更常见的是动态传入的集合参数,MyBatis提供了两种处理方式:

方式1:使用<foreach>标签

  1. <select id="getUsersByIdList" resultType="User">
  2. SELECT * FROM user
  3. WHERE id IN
  4. <foreach collection="idList" item="id" open="(" separator="," close=")">
  5. #{id}
  6. </foreach>
  7. </select>

参数传递

  1. List<Long> idList = Arrays.asList(1L, 2L, 3L);
  2. List<User> users = userMapper.getUsersByIdList(idList);

方式2:使用注解+@Param

  1. @Select({
  2. "<script>",
  3. "SELECT * FROM user",
  4. "WHERE id IN",
  5. "<foreach collection='ids' item='id' open='(' separator=',' close=')'>",
  6. "#{id}",
  7. "</foreach>",
  8. "</script>"
  9. })
  10. List<User> getUsersByIdList(@Param("ids") List<Long> ids);

关键点:必须使用<script>包裹动态SQL,否则会解析失败。

2.3 性能优化策略

当IN列表数据量较大时(如超过1000个),需考虑以下优化方案:

方案1:分批查询

  1. public List<User> batchQuery(List<Long> allIds) {
  2. List<User> result = new ArrayList<>();
  3. int batchSize = 500;
  4. for (int i = 0; i < allIds.size(); i += batchSize) {
  5. List<Long> subList = allIds.subList(i, Math.min(i + batchSize, allIds.size()));
  6. result.addAll(userMapper.getUsersByIdList(subList));
  7. }
  8. return result;
  9. }

方案2:临时表关联

对于超大数据集,可创建临时表存储ID列表:

  1. <insert id="createTempTable">
  2. CREATE TEMPORARY TABLE temp_ids (id BIGINT)
  3. </insert>
  4. <update id="insertTempIds">
  5. INSERT INTO temp_ids VALUES
  6. <foreach collection="idList" item="id" separator=",">
  7. (#{id})
  8. </foreach>
  9. </update>
  10. <select id="queryByTempTable" resultType="User">
  11. SELECT u.* FROM user u JOIN temp_ids t ON u.id = t.id
  12. </select>

三、高级应用场景

3.1 模糊查询与IN条件的组合使用

  1. <select id="complexSearch" resultType="User">
  2. SELECT * FROM user
  3. WHERE
  4. <if test="nameKeyword != null">
  5. name LIKE CONCAT('%', #{nameKeyword}, '%')
  6. </if>
  7. <if test="idList != null and idList.size() > 0">
  8. AND id IN
  9. <foreach collection="idList" item="id" open="(" separator="," close=")">
  10. #{id}
  11. </foreach>
  12. </if>
  13. </select>

3.2 数据库方言适配

不同数据库对模糊查询和IN条件的支持存在差异:

MySQL优化

  1. <!-- 使用FIND_IN_SET函数(仅适用于逗号分隔的字符串) -->
  2. <select id="findInSet" resultType="User">
  3. SELECT * FROM user
  4. WHERE FIND_IN_SET(id, #{idString})
  5. </select>

Oracle优化

  1. <!-- 使用INSTR函数实现模糊查询 -->
  2. <select id="searchByInstr" resultType="User">
  3. SELECT * FROM user
  4. WHERE INSTR(name, #{keyword}) > 0
  5. </select>

四、常见问题解决方案

4.1 IN条件参数为空的处理

当传入空集合时,直接执行会导致SQL语法错误。解决方案:

方案1:MyBatis拦截器

  1. @Intercepts({
  2. @Signature(type= StatementHandler.class, method="prepare", args={Connection.class, Integer.class})
  3. })
  4. public class EmptyInInterceptor implements Interceptor {
  5. @Override
  6. public Object intercept(Invocation invocation) throws Throwable {
  7. StatementHandler handler = (StatementHandler) invocation.getTarget();
  8. BoundSql boundSql = handler.getBoundSql();
  9. Object parameterObject = boundSql.getParameterObject();
  10. if (parameterObject instanceof Map) {
  11. Map<?, ?> paramMap = (Map<?, ?>) parameterObject;
  12. paramMap.entrySet().stream()
  13. .filter(e -> e.getValue() instanceof Collection && ((Collection) e.getValue()).isEmpty())
  14. .forEach(e -> {
  15. String sql = boundSql.getSql().toLowerCase();
  16. if (sql.contains("where") && sql.contains("in")) {
  17. throw new IllegalArgumentException("IN参数不能为空");
  18. }
  19. });
  20. }
  21. return invocation.proceed();
  22. }
  23. }

方案2:MyBatis-Plus解决方案

使用MyBatis-Plus的LambdaQueryWrapper自动处理空集合:

  1. List<Long> emptyList = new ArrayList<>();
  2. LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
  3. wrapper.in(User::getId, emptyList); // 自动忽略空集合
  4. List<User> users = userMapper.selectList(wrapper); // 返回空列表

4.2 模糊查询性能优化

对于高频模糊查询,建议:

  1. 使用数据库全文索引(如MySQL的FULLTEXT)
  2. 考虑引入Elasticsearch等搜索引擎
  3. 对长文本字段建立前缀索引

五、最佳实践总结

  1. 安全优先:模糊查询必须使用#{}参数绑定,严禁直接拼接
  2. 性能考量:IN列表超过500个元素时考虑分批处理
  3. 代码可读性:复杂查询优先使用XML配置而非注解
  4. 数据库适配:根据实际使用的数据库类型调整语法
  5. 异常处理:对空集合等边界情况做好防御性编程

通过合理运用这些技术,开发者可以构建出既安全又高效的数据库查询层,为系统性能提供坚实保障。在实际项目中,建议结合具体业务场景进行测试和调优,以达到最佳效果。

相关文章推荐

发表评论