一、MyBatis 简介
- 定义 :MyBatis 是一个 半自动化的 ORM(Object-Relational Mapping)框架,用于将 Java 对象与数据库记录进行映射。
- 特点 :
- 轻量级、灵活、易于掌握。
- SQL 与代码分离,支持动态 SQL。
- 相比 Hibernate 等全自动 ORM 框架,MyBatis 提供更高的 SQL 控制能力。
二、Spring Boot 整合 MyBatis 步骤
1. 添加依赖
在 pom.xml
中引入 MyBatis 启动器:
xml
深色版本
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.3</version> <!-- 推荐使用最新稳定版本 -->
</dependency>
⚠️ 注意:该依赖会自动包含
mybatis
、mybatis-spring
和自动配置模块。
2. 配置文件设置(application.yml)
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
type-aliases-package: com.example.demo.entity # 别名包路径
mapper-locations: classpath:mapper/*.xml # XML 映射文件位置
configuration:
map-underscore-to-camel-case: true # 开启驼峰命名自动映射 a_column -> aColumn
3. 编写 Mapper 接口
使用 @Mapper
注解标记接口,或在启动类上加 @MapperScan
扫描包。
@Mapper
public interface UserMapper {
User findById(Long id);
}
或在主启动类上:
@SpringBootApplication
@MapperScan("com.example.demo.mapper")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
4. 编写映射 XML 文件(Mapper XML)
文件路径:resources/mapper/UserMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserMapper">
<select id="findById" resultType="User">
SELECT * FROM user WHERE id = #{id}
</select>
</mapper>
✅ 注意:
namespace
必须对应 Mapper 接口的全限定名。id
在同一个 XML 中必须唯一,且与接口方法名一致。#{}
是预编译占位符,防止 SQL 注入;${}
是字符串拼接,需谨慎使用。
5. 安装 MyBatis-X 插件(推荐)
IntelliJ IDEA 插件:MyBatisX
- 快速跳转:Mapper 接口 ↔ XML 映射文件。
- 自动生成 CRUD 方法和 SQL 片段。
- 提高开发效率。
三、动态 SQL 与参数传递
MyBatis 使用 OGNL(Object-Graph Navigation Language)表达式 解析参数,通过反射获取对象属性值。
1. 单个参数(POJO 或 Map)
可以直接通过属性名访问:
public User findByName(User user); // user.getName()
<select id="findByName" resultType="User">
SELECT * FROM user WHERE name = #{name}
</select>
✅
#{name}
自动从 User 对象中调用getName()
获取值。
2. 多个参数(必须指定参数名)
Java 方法:
List<User> findUsers(@Param("name") String name, @Param("age") int age);
XML 使用:
<select id="findUsers" resultType="User">
SELECT * FROM user
WHERE name LIKE #{name} AND age > #{age}
</select>
❗ 若不使用
@Param
,多个参数需用param1
,param2
或arg0
,arg1
引用,可读性差,强烈建议使用 @Param 显式命名。
3. 使用 <bind>
标签构造新变量
用于模糊查询等场景:
<select id="findByNameLike" parameterType="string" resultType="User">
<bind name="pattern" value="'%' + _parameter + '%'" />
SELECT * FROM user WHERE name LIKE #{pattern}
</select>
或:
<bind name="nameLike" value="'%'+name+'%'"/>
AND name LIKE #{nameLike}
🔍 说明:
_parameter
表示整个参数对象。若参数是简单类型(如 String),则直接可用。
4. ${}
vs #{}
方式 | 是否预编译 | 是否防注入 | 适用场景 |
---|---|---|---|
#{} |
✅ 是 | ✅ 安全 | 绝大多数情况(推荐) |
${} |
❌ 否 | ❌ 易被注入 | 动态表名、排序字段等 |
示例:
ORDER BY ${columnName} <!-- 只能用 ${} -->
⚠️ 使用
${}
时务必校验输入合法性!
四、结果映射(ResultMap)
1. 手动映射字段(解决列名与属性不一致)
<resultMap id="userResultMap" type="User">
<id property="id" column="user_id"/>
<result property="name" column="user_name"/>
<result property="email" column="user_email"/>
</resultMap>
<select id="findAll" resultMap="userResultMap">
SELECT user_id, user_name, user_email FROM user
</select>
2. 关联查询(一对一、一对多)
(1)一对一关联
例如:User ↔ Profile
<resultMap id="userWithProfileMap" type="User">
<id property="id" column="id"/>
<result property="name" column="name"/>
<association property="profile" javaType="Profile">
<id property="id" column="pid"/>
<result property="bio" column="bio"/>
</association>
</resultMap>
<select id="findUserWithProfile" resultMap="userWithProfileMap">
SELECT u.id, u.name, p.id AS pid, p.bio
FROM user u
LEFT JOIN profile p ON u.id = p.user_id
WHERE u.id = #{id}
</select>
(2)一对多关联
例如:Department ↔ List<Employee>
<resultMap id="deptWithEmpsMap" type="Department">
<id property="id" column="dept_id"/>
<result property="name" column="dept_name"/>
<collection property="employees" ofType="Employee">
<id property="id" column="emp_id"/>
<result property="name" column="emp_name"/>
</collection>
</resultMap>
<select id="findDeptWithEmployees" resultMap="deptWithEmpsMap">
SELECT d.id AS dept_id, d.name AS dept_name,
e.id AS emp_id, e.name AS emp_name
FROM department d
LEFT JOIN employee e ON d.id = e.dept_id
WHERE d.id = #{id}
</select>
五、N+1 查询问题与性能优化
1. 什么是 N+1 问题?
当使用 association
或 collection
延迟加载时,MyBatis 可能先查主表(1次),再对每条记录发起关联查询(N次),共 N+1 次 SQL。
2. 解决方案
✅ 方案一:使用 联合查询 + 嵌套映射(推荐)
如上一对多示例,一次性 JOIN 查询,避免 N+1。
✅ 方案二:开启 延迟加载(Lazy Loading)
mybatis:
configuration:
lazy-loading-enabled: true
aggressive-lazy-loading: false # false:仅加载被调用的属性
⚠️ 延迟加载会创建新会话,一级缓存失效。
✅ 方案三:使用 二级缓存
提升跨会话的数据复用性。
开启步骤:
-
全局开启缓存:
mybatis:
configuration:
cache-enabled: true -
在 Mapper XML 中声明缓存:
<mapper namespace="com.example.mapper.UserMapper"> <cache/><select id="findById" useCache="true" resultType="User"> SELECT * FROM user WHERE id = #{id} </select> <update id="updateUser" flushCache="true"> UPDATE user SET name = #{name} WHERE id = #{id} </update>
useCache="true"
:查询结果放入二级缓存(默认 select 为 true)。flushCache="true"
:执行后清空缓存(默认 insert/update/delete 为 true)。- 实体类需实现
Serializable
接口。
六、自动分页(集成 PageHelper)
1. 添加依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.7</version>
</dependency>
2. 使用
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public PageInfo<User> getUsers(int pageNum, int pageSize) {
PageHelper.startPage(pageNum, pageSize);
List<User> users = userMapper.findAll();
return new PageInfo<>(users);
}
}
返回 JSON 示例:
{
"pageNum": 1,
"pageSize": 10,
"total": 100,
"pages": 10,
"list": [...]
}
七、补充知识点
1. @RestController
与 @ResponseBody
@RestController = @Controller + @ResponseBody
- 所有方法返回值直接作为响应体(JSON/XML),无需额外标注
@ResponseBody
。
2. 类型处理器(TypeHandler)
自定义 Java 类型与数据库类型的转换,如枚举、LocalDateTime 等。
@MappedTypes(LocalDate.class)
@MappedJdbcTypes(JdbcType.DATE)
public class LocalDateTypeHandler extends BaseTypeHandler<LocalDate> {
// 实现 setParameter、getResult 方法
}
注册方式:
@Configuration
public class MyBatisConfig {
@Bean
public ConfigurationCustomizer configurationCustomizer() {
return configuration -> configuration.getTypeHandlerRegistry()
.register(LocalDate.class, new LocalDateTypeHandler());
}
}
八、总结
功能 | 推荐做法 |
---|---|
参数传递 | 多参数用 @Param 显式命名 |
模糊查询 | 用 <bind> 构造 pattern,配合 #{} |
字段映射 | 列名转驼峰开启 map-underscore-to-camel-case |
关联查询 | 优先使用 JOIN + resultMap 避免 N+1 |
性能优化 | 合理使用一级/二级缓存、延迟加载 |
分页 | 集成 PageHelper |
开发效率 | 安装 MyBatisX 插件 |