MyBatis
MyBatis 持久层基础
MyBatis 把 Mapper 接口方法、SQL 定义、参数绑定和结果映射组织起来,减少手写 JDBC 样板代码,但 SQL 设计和事务边界仍由开发者负责。
Spring Boot 快速接入
准备步骤:
- 引入 MyBatis Starter 与数据库驱动。
- 准备数据表和 Java 实体。
- 配置
DataSource。 - 定义 Mapper 接口。
- 使用注解或 XML 编写 SQL。
代码块收起展开
@Mapper
public interface UserMapper {
@Select("select id, username, name, age from user")
List<User> findAll();
}测试类使用 @SpringBootTest 时会加载 Spring Boot 容器;测试包通常放在启动类所在包或其子包下。
MyBatis 与 JDBC
MyBatis 仍然使用 JDBC:
- 从
DataSource获取连接。 - 根据 Mapper 方法找到 SQL 定义。
- 生成
PreparedStatement并绑定参数。 - 执行 SQL。
- 将
ResultSet映射为 Java 对象。
它不会替你决定索引、连接方式、锁、事务隔离级别或 SQL 是否高效。
基本 CRUD
代码块收起展开
@Delete("delete from user where id = #{id}")
int deleteById(Long id);
@Insert("""
insert into user(username, password, name, age)
values(#{username}, #{password}, #{name}, #{age})
""")
@Options(useGeneratedKeys = true, keyProperty = "id")
int insert(User user);
@Update("""
update user
set username = #{username},
name = #{name},
age = #{age}
where id = #{id}
""")
int update(User user);DML 方法建议返回受影响行数,用于判断目标是否存在以及乐观更新是否成功。
#{...} 与 ${...}
| 语法 | 机制 | 适用范围 | 风险 |
|---|
代码块收起展开
| `#{value}` | 预编译占位符 | 普通参数值 | 安全,默认使用 |
| `${value}` | 文本拼接 | 表名、列名等结构片段 | 有 SQL 注入风险 |表名、排序列等结构不能由 ? 绑定,必须在服务端映射白名单:
代码块收起展开
String orderBy = switch (sort) {
case "name" -> "name";
case "createdAt" -> "create_time";
default -> "id";
};不要直接把用户输入交给 ${sort}。
多参数与 @Param
代码块收起展开
@Select("""
select id, username, name
from user
where username = #{username}
and password = #{password}
""")
User findByCredentials(
@Param("username") String username,
@Param("password") String password);@Param 为参数建立稳定名称,避免依赖编译器是否保留形参名。真实系统不应明文保存或查询密码,应保存慢哈希并在应用层验证。
XML 映射
复杂 SQL 更适合 XML。默认约定:
- XML 与 Mapper 同名。
namespace是 Mapper 全限定名。- SQL 的
id与接口方法名一致。 resultType或resultMap与返回结构匹配。
代码块收起展开
<mapper namespace="com.example.mapper.UserMapper">
<select id="findAll" resultType="com.example.pojo.User">
select id, username, name, age
from user
</select>
</mapper>结果映射
数据库 create_time 与 Java createTime 可通过以下方式对应:
- SQL 别名:
create_time as createTime。 - 开启下划线转驼峰:
mybatis.configuration.map-underscore-to-camel-case: true。 - 使用
resultMap显式映射。
简单平铺对象用 resultType;字段名不一致、嵌套对象、一对多集合等复杂结构用 resultMap。
连接池
Spring Boot 默认通常使用 HikariCP,也可以显式切换 Druid。MyBatis 使用容器中的 DataSource,业务代码不应自行重复创建驱动连接。
常见失误
- Mapper XML 的 namespace 或 id 与接口不一致。
- 动态 SQL 末尾多出逗号、AND 或分号。
- 查询列缺少别名,导致属性没有回填。
- 在循环中逐条查询,形成 N+1。
- 使用
${}拼接不可信输入。 - 只依赖日志看到 SQL,却没有检查真实参数、执行计划和受影响行数。