MyBatis进阶技巧:模糊查询与IN条件的高效实现
2025.09.19 16:32浏览量:0简介:本文深入探讨MyBatis框架中模糊查询与IN条件查询的实现方法,从基础语法到性能优化,提供可落地的技术方案。
MyBatis进阶技巧:模糊查询与IN条件的高效实现
一、模糊查询的核心实现方式
1.1 基础LIKE语法实现
MyBatis中实现模糊查询最直接的方式是通过XML映射文件中的LIKE语句。在SQL语句中,通常需要结合#{}
或${}
参数占位符使用:
<!-- 方式1:前端拼接通配符 -->
<select id="searchByName" resultType="User">
SELECT * FROM user
WHERE name LIKE CONCAT('%', #{keyword}, '%')
</select>
<!-- 方式2:后端拼接通配符(需注意SQL注入) -->
<select id="searchByNameUnsafe" resultType="User">
SELECT * FROM user
WHERE name LIKE '%${keyword}%'
</select>
关键区别:#{}
会进行预编译处理,有效防止SQL注入;而${}
直接拼接字符串,存在安全风险。建议优先使用#{}
配合数据库函数实现。
1.2 动态SQL的灵活应用
对于多条件模糊查询场景,MyBatis的动态SQL标签能显著提升代码可维护性:
<select id="advancedSearch" resultType="User">
SELECT * FROM user
<where>
<if test="name != null">
AND name LIKE CONCAT('%', #{name}, '%')
</if>
<if test="email != null">
AND email LIKE CONCAT('%', #{email}, '%')
</if>
</where>
</select>
最佳实践:使用<where>
标签自动处理AND/OR前缀,避免手动拼接时出现的语法错误。
1.3 注解方式实现
对于简单场景,可通过@Select
注解直接编写SQL:
@Select("SELECT * FROM user WHERE name LIKE CONCAT('%', #{keyword}, '%')")
List<User> searchByName(@Param("keyword") String keyword);
适用场景:单表简单查询,复杂场景仍建议使用XML配置。
二、IN条件查询的深度解析
2.1 静态IN列表处理
当IN条件值固定时,可直接在XML中编写:
<select id="getUsersByIds" resultType="User">
SELECT * FROM user
WHERE id IN (1, 2, 3, 5, 8)
</select>
2.2 动态IN列表实现
实际开发中更常见的是动态传入的集合参数,MyBatis提供了两种处理方式:
方式1:使用<foreach>
标签
<select id="getUsersByIdList" resultType="User">
SELECT * FROM user
WHERE id IN
<foreach collection="idList" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</select>
参数传递:
List<Long> idList = Arrays.asList(1L, 2L, 3L);
List<User> users = userMapper.getUsersByIdList(idList);
方式2:使用注解+@Param
@Select({
"<script>",
"SELECT * FROM user",
"WHERE id IN",
"<foreach collection='ids' item='id' open='(' separator=',' close=')'>",
"#{id}",
"</foreach>",
"</script>"
})
List<User> getUsersByIdList(@Param("ids") List<Long> ids);
关键点:必须使用<script>
包裹动态SQL,否则会解析失败。
2.3 性能优化策略
当IN列表数据量较大时(如超过1000个),需考虑以下优化方案:
方案1:分批查询
public List<User> batchQuery(List<Long> allIds) {
List<User> result = new ArrayList<>();
int batchSize = 500;
for (int i = 0; i < allIds.size(); i += batchSize) {
List<Long> subList = allIds.subList(i, Math.min(i + batchSize, allIds.size()));
result.addAll(userMapper.getUsersByIdList(subList));
}
return result;
}
方案2:临时表关联
对于超大数据集,可创建临时表存储ID列表:
<insert id="createTempTable">
CREATE TEMPORARY TABLE temp_ids (id BIGINT)
</insert>
<update id="insertTempIds">
INSERT INTO temp_ids VALUES
<foreach collection="idList" item="id" separator=",">
(#{id})
</foreach>
</update>
<select id="queryByTempTable" resultType="User">
SELECT u.* FROM user u JOIN temp_ids t ON u.id = t.id
</select>
三、高级应用场景
3.1 模糊查询与IN条件的组合使用
<select id="complexSearch" resultType="User">
SELECT * FROM user
WHERE
<if test="nameKeyword != null">
name LIKE CONCAT('%', #{nameKeyword}, '%')
</if>
<if test="idList != null and idList.size() > 0">
AND id IN
<foreach collection="idList" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</if>
</select>
3.2 数据库方言适配
不同数据库对模糊查询和IN条件的支持存在差异:
MySQL优化
<!-- 使用FIND_IN_SET函数(仅适用于逗号分隔的字符串) -->
<select id="findInSet" resultType="User">
SELECT * FROM user
WHERE FIND_IN_SET(id, #{idString})
</select>
Oracle优化
<!-- 使用INSTR函数实现模糊查询 -->
<select id="searchByInstr" resultType="User">
SELECT * FROM user
WHERE INSTR(name, #{keyword}) > 0
</select>
四、常见问题解决方案
4.1 IN条件参数为空的处理
当传入空集合时,直接执行会导致SQL语法错误。解决方案:
方案1:MyBatis拦截器
@Intercepts({
@Signature(type= StatementHandler.class, method="prepare", args={Connection.class, Integer.class})
})
public class EmptyInInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler handler = (StatementHandler) invocation.getTarget();
BoundSql boundSql = handler.getBoundSql();
Object parameterObject = boundSql.getParameterObject();
if (parameterObject instanceof Map) {
Map<?, ?> paramMap = (Map<?, ?>) parameterObject;
paramMap.entrySet().stream()
.filter(e -> e.getValue() instanceof Collection && ((Collection) e.getValue()).isEmpty())
.forEach(e -> {
String sql = boundSql.getSql().toLowerCase();
if (sql.contains("where") && sql.contains("in")) {
throw new IllegalArgumentException("IN参数不能为空");
}
});
}
return invocation.proceed();
}
}
方案2:MyBatis-Plus解决方案
使用MyBatis-Plus的LambdaQueryWrapper
自动处理空集合:
List<Long> emptyList = new ArrayList<>();
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.in(User::getId, emptyList); // 自动忽略空集合
List<User> users = userMapper.selectList(wrapper); // 返回空列表
4.2 模糊查询性能优化
对于高频模糊查询,建议:
- 使用数据库全文索引(如MySQL的FULLTEXT)
- 考虑引入Elasticsearch等搜索引擎
- 对长文本字段建立前缀索引
五、最佳实践总结
- 安全优先:模糊查询必须使用
#{}
参数绑定,严禁直接拼接 - 性能考量:IN列表超过500个元素时考虑分批处理
- 代码可读性:复杂查询优先使用XML配置而非注解
- 数据库适配:根据实际使用的数据库类型调整语法
- 异常处理:对空集合等边界情况做好防御性编程
通过合理运用这些技术,开发者可以构建出既安全又高效的数据库查询层,为系统性能提供坚实保障。在实际项目中,建议结合具体业务场景进行测试和调优,以达到最佳效果。
发表评论
登录后可评论,请前往 登录 或 注册