简介:本文详细解析MyBatis框架中模糊查询与IN条件查询的实现方法,结合XML映射文件与注解开发两种方式,提供完整代码示例与性能优化建议。
MyBatis的模糊查询主要通过SQL语句中的LIKE关键字实现,结合动态SQL功能可构建灵活的查询条件。在MyBatis中实现模糊查询有两种典型方式:
<!-- UserMapper.xml --><select id="selectByNameLike" resultType="User">SELECT * FROM userWHERE name LIKE CONCAT('%', #{keyword}, '%')</select>
这种方式通过MySQL的CONCAT函数直接拼接通配符,适用于简单场景。但存在SQL注入风险,需确保参数经过严格校验。
<select id="selectByDynamicPattern" resultType="User">SELECT * FROM userWHERE name LIKE<bind name="pattern" value="'%' + keyword + '%'" />#{pattern}</select>
或使用<if>标签构建更复杂的条件:
<select id="searchUsers" resultType="User">SELECT * FROM userWHERE 1=1<if test="name != null">AND name LIKE CONCAT('%', #{name}, '%')</if><if test="email != null">AND email LIKE '%' || #{email} || '%'</if></select>
关键点:
CONCAT,Oracle用||)<bind>标签预处理参数,提升可读性<where>标签自动处理AND/OR前缀IN查询用于匹配多个离散值,在MyBatis中有三种实现方式:
<select id="selectByIds" resultType="User">SELECT * FROM userWHERE id IN (1, 2, 3, 5, 8)</select>
适用于固定值列表的场景,但实际开发中极少使用。
// Mapper接口List<User> selectByIdList(@Param("ids") List<Long> ids);
<select id="selectByIdList" resultType="User">SELECT * FROM userWHERE 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 userWHERE<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>
@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>标签包裹动态SQLLIKE '%keyword'无法使用索引,应设计为LIKE 'keyword%'或全字段匹配CREATE INDEX idx_name_fuzzy ON user((name))
-- 创建临时表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分批处理#{}而非${}处理参数
# application.propertieslogging.level.org.mybatis=DEBUG
<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>
@Transactionalpublic 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 userSET status = #{newStatus},update_time = #{updateTime}WHERE id IN<foreach item="id" collection="ids" open="(" separator="," close=")">#{id}</foreach></update>
原因分析:
解决方案:
<!-- 强制转换为小写比较 --><select id="caseInsensitiveSearch" resultType="User">SELECT * FROM userWHERE LOWER(name) LIKE CONCAT('%', LOWER(#{keyword}), '%')</select>
错误表现:
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;}
对于使用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和数据库特性,可显著提升查询效率与系统稳定性。