MyBatis模糊查询与IN条件操作全解析
2025.09.19 15:54浏览量:0简介:本文详细解析MyBatis框架中模糊查询与IN条件查询的实现方法,结合XML映射文件与注解开发两种方式,提供完整代码示例与性能优化建议。
MyBatis模糊查询与IN条件操作全解析
一、模糊查询实现原理与核心方法
MyBatis的模糊查询主要通过SQL语句中的LIKE
关键字实现,结合动态SQL功能可构建灵活的查询条件。在MyBatis中实现模糊查询有两种典型方式:
1.1 参数直接拼接方式
<!-- UserMapper.xml -->
<select id="selectByNameLike" resultType="User">
SELECT * FROM user
WHERE name LIKE CONCAT('%', #{keyword}, '%')
</select>
这种方式通过MySQL的CONCAT
函数直接拼接通配符,适用于简单场景。但存在SQL注入风险,需确保参数经过严格校验。
1.2 动态SQL绑定方式(推荐)
<select id="selectByDynamicPattern" resultType="User">
SELECT * FROM user
WHERE name LIKE
<bind name="pattern" value="'%' + keyword + '%'" />
#{pattern}
</select>
或使用<if>
标签构建更复杂的条件:
<select id="searchUsers" resultType="User">
SELECT * FROM user
WHERE 1=1
<if test="name != null">
AND name LIKE CONCAT('%', #{name}, '%')
</if>
<if test="email != null">
AND email LIKE '%' || #{email} || '%'
</if>
</select>
关键点:
- 不同数据库的字符串拼接语法不同(MySQL用
CONCAT
,Oracle用||
) - 推荐使用
<bind>
标签预处理参数,提升可读性 - 动态SQL可结合
<where>
标签自动处理AND/OR前缀
二、IN条件查询的深度实践
IN查询用于匹配多个离散值,在MyBatis中有三种实现方式:
2.1 静态IN列表
<select id="selectByIds" resultType="User">
SELECT * FROM user
WHERE id IN (1, 2, 3, 5, 8)
</select>
适用于固定值列表的场景,但实际开发中极少使用。
2.2 动态IN列表(数组/集合)
// Mapper接口
List<User> selectByIdList(@Param("ids") List<Long> ids);
<select id="selectByIdList" resultType="User">
SELECT * FROM user
WHERE id IN
<foreach item="id" collection="ids" open="(" separator="," close=")">
#{id}
</foreach>
</select>
实现细节:
collection
属性对应方法参数名,若使用@Param
注解则写注解值item
定义循环变量名,open/close
定义括号,separator
定义分隔符- 当集合为空时,默认会生成
WHERE id IN ()
导致语法错误,需配合<if>
处理:<select id="safeSelectByIdList" resultType="User">
SELECT * FROM user
WHERE
<if test="ids != null and ids.size() > 0">
id IN
<foreach item="id" collection="ids" open="(" separator="," close=")">
#{id}
</foreach>
</if>
<if test="ids == null or ids.size() == 0">
1=0 <!-- 返回空结果集 -->
</if>
</select>
2.3 注解方式实现IN查询
@Select({
"<script>",
"SELECT * FROM user",
"WHERE id IN",
"<foreach item='id' collection='ids' open='(' separator=',' close=')'>",
"#{id}",
"</foreach>",
"</script>"
})
List<User> selectByIdListAnnotated(@Param("ids") List<Long> ids);
注意事项:
- 必须使用
<script>
标签包裹动态SQL - 注解方式对复杂SQL可读性较差,建议简单场景使用
三、性能优化与最佳实践
3.1 模糊查询优化策略
- 避免前导通配符:
LIKE '%keyword'
无法使用索引,应设计为LIKE 'keyword%'
或全字段匹配 - 全文索引替代:对长文本字段,考虑使用MySQL的FULLTEXT索引或Elasticsearch
- 函数索引:MySQL 8.0+支持函数索引,可创建
CREATE INDEX idx_name_fuzzy ON user((name))
3.2 IN查询优化方案
- 批量查询限制:建议单次IN列表不超过1000个值(不同数据库限制不同)
- 临时表方案:大数据量时改用临时表关联
-- 创建临时表
CREATE TEMPORARY TABLE temp_ids (id BIGINT);
-- 批量插入
INSERT INTO temp_ids VALUES (1),(2),(3);
-- 关联查询
SELECT u.* FROM user u JOIN temp_ids t ON u.id = t.id;
- 分页处理:结合
ROW_NUMBER()
或LIMIT
分批处理
3.3 动态SQL编写规范
- XML格式化:使用4空格缩进,保持结构清晰
- SQL注入防护:
- 避免直接拼接SQL片段
- 使用
#{}
而非${}
处理参数
- 日志调试:开启MyBatis日志查看最终执行的SQL
# application.properties
logging.level.org.mybatis=DEBUG
四、典型应用场景案例
4.1 多条件组合查询
<select id="complexSearch" resultType="User">
SELECT * FROM user
<where>
<if test="name != null">
AND name LIKE CONCAT('%', #{name}, '%')
</if>
<if test="statusList != null and statusList.size() > 0">
AND status IN
<foreach item="status" collection="statusList" open="(" separator="," close=")">
#{status}
</foreach>
</if>
<if test="minAge != null">
AND age >= #{minAge}
</if>
</where>
ORDER BY create_time DESC
</select>
4.2 批量操作与事务控制
@Transactional
public void batchUpdateStatus(List<Long> ids, Integer newStatus) {
// 先查询存在性(可选)
List<User> users = userMapper.selectByIdList(ids);
if(users.size() != ids.size()) {
throw new RuntimeException("部分ID不存在");
}
// 批量更新
userMapper.batchUpdateStatus(
ids,
newStatus,
new Date() // 更新时间
);
}
<update id="batchUpdateStatus">
UPDATE user
SET status = #{newStatus},
update_time = #{updateTime}
WHERE id IN
<foreach item="id" collection="ids" open="(" separator="," close=")">
#{id}
</foreach>
</update>
五、常见问题解决方案
5.1 模糊查询返回空结果
原因分析:
- 参数未正确传递
- 数据库字符集不匹配(如UTF-8 vs Latin1)
- 大小写敏感问题
解决方案:
<!-- 强制转换为小写比较 -->
<select id="caseInsensitiveSearch" resultType="User">
SELECT * FROM user
WHERE LOWER(name) LIKE CONCAT('%', LOWER(#{keyword}), '%')
</select>
5.2 IN查询参数过多
错误表现:
ORA-01795: maximum number of expressions in a list is 1000
解决方案:
- 分批查询:
public List<User> selectInBatches(List<Long> allIds, int batchSize) {
List<User> result = new ArrayList<>();
for(int i=0; i<allIds.size(); i+=batchSize) {
List<Long> batch = allIds.subList(i, Math.min(i+batchSize, allIds.size()));
result.addAll(userMapper.selectByIdList(batch));
}
return result;
}
- 使用JOIN替代IN
六、进阶技巧:MyBatis-Plus集成
对于使用MyBatis-Plus的项目,可简化操作:
// 模糊查询
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.like(User::getName, "张")
.in(User::getStatus, Arrays.asList(1, 2, 3));
List<User> users = userMapper.selectList(wrapper);
// 批量操作
userMapper.update(
null,
new LambdaUpdateWrapper<User>()
.set(User::getStatus, 2)
.in(User::getId, Arrays.asList(1L, 2L, 3L))
);
总结:本文系统阐述了MyBatis中模糊查询与IN条件查询的实现方法,从基础语法到性能优化提供了完整解决方案。实际开发中,应结合业务场景选择合适方案,特别注意SQL注入防护和大数据量处理。通过合理使用动态SQL和数据库特性,可显著提升查询效率与系统稳定性。
发表评论
登录后可评论,请前往 登录 或 注册