1. 为什么我们需要"最最最最最最简单"的条件查询
在Java持久层框架中,MyBatis以其灵活性和直观性著称,但新手常常被其强大的功能所震慑。我见过太多开发者第一次接触MyBatis时,面对动态SQL、结果映射、缓存配置等复杂特性,连最基本的条件查询都要翻半天文档。实际上,80%的日常查询场景只需要最基础的WHERE条件就能满足。
上周我团队来了个实习生,他花了整整两天时间研究如何用MyBatis实现一个"按状态筛选订单"的功能。当我看到他写的包含
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 准备你的第一个条件查询环境
2.1 最小化MyBatis配置
首先确保你的pom.xml包含最新稳定版依赖(截至2023年8月):
xml复制<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.11</version>
</dependency>
创建最基本的SqlSessionFactory配置(省略了数据源部分):
java复制String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
对应的mybatis-config.xml只需要这些内容:
xml复制<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!-- 你的数据源配置 -->
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
</configuration>
2.2 实体类与表结构对应
假设我们要查询用户表,先定义最简单的POJO:
java复制public class User {
private Integer id;
private String username;
private Integer status; // 0-禁用 1-启用
// 省略getter/setter
}
对应的users表结构:
sql复制CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL,
status TINYINT DEFAULT 1
);
3. 实现基础条件查询的三种方式
3.1 XML映射文件方式
在UserMapper.xml中定义最简查询:
xml复制<select id="selectByStatus" resultType="com.example.User">
SELECT * FROM users WHERE status = #{status}
</select>
对应的Mapper接口:
java复制public interface UserMapper {
List<User> selectByStatus(@Param("status") Integer status);
}
调用示例:
java复制try (SqlSession session = sqlSessionFactory.openSession()) {
UserMapper mapper = session.getMapper(UserMapper.class);
List<User> activeUsers = mapper.selectByStatus(1);
}
3.2 注解方式实现
不喜欢XML的开发者可以直接用注解:
java复制@Select("SELECT * FROM users WHERE status = #{status}")
List<User> selectByStatus(Integer status);
3.3 动态条件拼接
虽然标题强调"最简单",但实际项目中难免需要稍微灵活一点的查询。这里给出一个既简单又实用的方案:
xml复制<select id="selectUsers" resultType="com.example.User">
SELECT * FROM users
<where>
<if test="status != null">
AND status = #{status}
</if>
<if test="username != null">
AND username LIKE CONCAT('%',#{username},'%')
</if>
</where>
</select>
这个查询的神奇之处在于:
<where>标签会自动处理AND前缀- 当status为null时,对应的条件不会出现在SQL中
- 两个条件可以任意组合
4. 那些年我们踩过的条件查询坑
4.1 参数绑定陷阱
新手常犯的错误是混淆#{}和${}:
xml复制<!-- 危险!存在SQL注入风险 -->
<select id="unsafeQuery" resultType="User">
SELECT * FROM users WHERE username = '${username}'
</select>
<!-- 正确做法 -->
<select id="safeQuery" resultType="User">
SELECT * FROM users WHERE username = #{username}
</select>
我曾经在代码审计中发现,某系统因为开发人员混用这两种语法,导致用户表全部泄露。记住:#{}是预编译参数,${}是直接字符串替换。
4.2 特殊字符转义
查询条件中包含特殊字符时:
xml复制<!-- 错误写法(XML解析会报错) -->
<select id="badExample" resultType="User">
SELECT * FROM users WHERE create_time < '2023-01-01'
</select>
<!-- 正确写法 -->
<select id="goodExample" resultType="User">
SELECT * FROM users WHERE create_time < '2023-01-01'
</select>
更现代的写法是使用CDATA:
xml复制<select id="bestExample" resultType="User">
<![CDATA[
SELECT * FROM users WHERE create_time < '2023-01-01'
]]>
</select>
4.3 枚举类型处理
当查询条件使用Java枚举时:
java复制public enum UserStatus {
DISABLED(0), ENABLED(1);
private final int code;
// 构造方法和getter
}
Mapper接口应该这样定义:
java复制List<User> selectByStatus(@Param("status") UserStatus status);
然后在XML中:
xml复制<select id="selectByStatus" resultType="User">
SELECT * FROM users WHERE status = #{status.code}
</select>
5. 性能优化小技巧
5.1 限制查询字段
即使需要条件查询,也不应该总是SELECT *:
xml复制<select id="selectLightweightUsers" resultType="User">
SELECT id, username FROM users WHERE status = #{status}
</select>
建议为不同场景定义不同的结果映射,避免传输不必要的数据。我曾经优化过一个查询,仅仅是把SELECT *改为明确字段列表,响应时间就从120ms降到了40ms。
5.2 分页查询优化
结合条件查询的最简分页实现:
xml复制<select id="selectByStatusWithPage" resultType="User">
SELECT * FROM users WHERE status = #{status}
LIMIT #{pageSize} OFFSET #{offset}
</select>
对应的Java调用:
java复制List<User> users = mapper.selectByStatusWithPage(1, 10, 0); // 第一页,每页10条
注意:对于大数据量表,更好的做法是使用基于游标的分页。
5.3 二级缓存慎用
在mybatis-config.xml中添加:
xml复制<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
然后在Mapper XML中:
xml复制<cache/>
但要注意:缓存会使条件查询结果可能不是最新的数据库状态。我曾在电商系统中遇到过因为不当使用缓存,导致用户看到的库存数量不更新的问题。
6. 调试与日志输出
6.1 查看实际执行的SQL
在mybatis-config.xml中配置:
xml复制<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
或者在logback.xml中:
xml复制<logger name="com.example.mapper" level="DEBUG"/>
这样你就能在控制台看到预处理语句和参数:
code复制DEBUG [main] - ==> Preparing: SELECT * FROM users WHERE status = ?
DEBUG [main] - ==> Parameters: 1(Integer)
6.2 结果集映射检查
当查询结果与预期不符时,可以临时改为返回Map:
java复制@Select("SELECT * FROM users WHERE id = #{id}")
Map<String, Object> selectUserAsMap(Integer id);
这样可以直观看到数据库返回的原始字段名和值,帮助诊断是查询问题还是结果映射问题。
7. 从简单走向实用
当你掌握了基础条件查询后,可以逐步尝试:
- 使用
片段复用公共SQL部分 - 学习association和collection处理关联查询
- 探索MyBatis Generator自动生成基础查询
- 了解MyBatis-Plus等增强工具
但记住:不要过度设计。我见过一个只有5张表的小系统,因为使用了大量动态SQL和复杂映射,维护成本反而比直接写JDBC还高。
