1. MyBatis条件查询入门指南
作为Java开发者最常用的ORM框架之一,MyBatis以其灵活性和易用性著称。今天我要分享的是如何用MyBatis实现最简单的条件查询——这个看似基础的功能,在实际开发中却藏着不少值得注意的细节。
先说说为什么条件查询如此重要。在日常业务中,超过80%的数据库操作都涉及某种形式的条件筛选。从用户管理系统的"按姓名搜索"到电商平台的"价格区间过滤",条件查询无处不在。而MyBatis提供了多种实现方式,每种都有其适用场景和性能考量。
提示:虽然标题强调"最简单",但我会带你从基础实现延伸到生产环境中的优化技巧,这些内容都是我在电商和金融系统开发中积累的实战经验。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 基础环境搭建
2.1 项目初始化
首先确保你的项目已经包含MyBatis核心依赖。以Maven项目为例:
xml复制<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
如果你使用Spring Boot,可以直接引入starter:
xml复制<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
2.2 数据库准备
我们以一个简单的用户表为例:
sql复制CREATE TABLE `user` (
`id` int NOT NULL AUTO_INCREMENT,
`username` varchar(50) DEFAULT NULL,
`age` int DEFAULT NULL,
`email` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`)
);
3. 最简条件查询实现
3.1 XML映射文件写法
在resources/mapper目录下创建UserMapper.xml:
xml复制<mapper namespace="com.example.mapper.UserMapper">
<select id="selectByCondition" resultType="com.example.entity.User">
SELECT * FROM user
<where>
<if test="username != null">
AND username = #{username}
</if>
<if test="age != null">
AND age = #{age}
</if>
</where>
</select>
</mapper>
这里有几个关键点:
<where>标签会自动处理AND前缀,避免SQL语法错误<if>条件判断使查询参数变为可选#{}是MyBatis的参数占位符,能防止SQL注入
3.2 接口定义
对应的Mapper接口:
java复制public interface UserMapper {
List<User> selectByCondition(@Param("username") String username,
@Param("age") Integer age);
}
3.3 服务层调用
java复制@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public List<User> queryUsers(String username, Integer age) {
return userMapper.selectByCondition(username, age);
}
}
4. 条件查询的进阶技巧
4.1 动态SQL的其他标签
除了<if>,MyBatis还提供了更多动态SQL标签:
xml复制<select id="selectAdvanced" resultType="User">
SELECT * FROM user
<where>
<choose>
<when test="username != null">
AND username LIKE CONCAT('%',#{username},'%')
</when>
<when test="email != null">
AND email = #{email}
</when>
<otherwise>
AND status = 1
</otherwise>
</choose>
<foreach collection="ids" item="id" open="AND id IN (" close=")" separator=",">
#{id}
</foreach>
</where>
</select>
4.2 参数传递的多种方式
除了直接传递参数,还可以使用Map或JavaBean:
java复制// Map传参
List<User> selectByMap(Map<String, Object> params);
// JavaBean传参
List<User> selectByBean(UserQuery query);
对应的XML:
xml复制<select id="selectByBean" resultType="User" parameterType="UserQuery">
SELECT * FROM user
<where>
<if test="minAge != null">
AND age >= #{minAge}
</if>
<if test="maxAge != null">
AND age <= #{maxAge}
</if>
</where>
</select>
5. 生产环境优化建议
5.1 SQL注入防护
虽然MyBatis的#{}已经能防止大部分SQL注入,但仍需注意:
- 不要使用
${}进行表名或列名的动态替换 - LIKE查询应使用CONCAT函数而非直接拼接
- 数值型参数要进行类型校验
5.2 性能优化
- 为常用查询条件添加数据库索引
- 避免在条件中使用函数计算,如
YEAR(create_time) = 2023 - 大数据量查询考虑分页实现
5.3 日志调试
在application.properties中添加:
properties复制mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
logging.level.com.example.mapper=DEBUG
这样可以在控制台看到实际执行的SQL语句,方便调试。
6. 常见问题排查
6.1 参数为null时的处理
当参数为null时,<if>判断会跳过该条件。如果需要区分null和空字符串,可以这样写:
xml复制<if test="username != null and username != ''">
6.2 特殊符号转义
XML中的特殊符号(<,>,&)需要转义,或者使用CDATA:
xml复制<if test="age < 18"> <!-- 错误 -->
<if test="age < 18"> <!-- 正确 -->
<![CDATA[
AND age < 18
]]>
6.3 枚举类型处理
处理枚举参数时,建议使用自定义类型处理器或在Java代码中转换为实际值:
java复制public enum UserStatus {
ACTIVE(1), INACTIVE(0);
private final int code;
// getter...
}
// 在Mapper中
List<User> selectByStatus(@Param("status") UserStatus status);
XML中:
xml复制<if test="status != null">
AND status = #{status.code}
</if>
7. 实际案例分享
最近在电商项目中实现了一个商品筛选功能,需要处理多达20种可能的筛选条件。最终采用的方案是:
- 使用JavaBean封装所有筛选参数
- 在XML中使用组合条件判断
- 对高频查询条件建立联合索引
- 对结果进行二级缓存
核心代码片段:
xml复制<select id="selectProducts" resultType="Product" parameterType="ProductQuery">
SELECT * FROM product
<where>
<if test="categoryId != null">
AND category_id = #{categoryId}
</if>
<if test="minPrice != null and maxPrice != null">
AND price BETWEEN #{minPrice} AND #{maxPrice}
</if>
<if test="keywords != null and keywords != ''">
AND (title LIKE CONCAT('%',#{keywords},'%')
OR description LIKE CONCAT('%',#{keywords},'%'))
</if>
<!-- 更多条件... -->
</where>
ORDER BY
<choose>
<when test="sortBy == 'price'">price</when>
<when test="sortBy == 'sales'">sales_count</when>
<otherwise>create_time</otherwise>
</choose>
<if test="sortOrder == 'desc'">DESC</if>
</select>
这个实现既保持了灵活性,又通过合理的索引设计保证了性能。上线后平均查询时间控制在50ms以内,即使在大促期间也能稳定运行。
8. 扩展思考
虽然MyBatis的条件查询已经很强大,但在复杂业务场景下,我们还可以考虑:
- 使用MyBatis-Plus的Wrapper条件构造器
- 结合QueryDSL实现类型安全的查询
- 对于超复杂查询,可以考虑使用存储过程
不过,这些高级用法都有其适用场景,不是越复杂越好。在大多数情况下,本文介绍的基础条件查询已经能满足80%的业务需求。
最后分享一个实用技巧:在开发阶段,可以使用MyBatis Log Plugin插件来格式化控制台输出的SQL,这样调试起来更加直观。只需要在IDEA插件市场搜索安装即可。
