思途Mybatis学习 0805
一、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%2B8username: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Drivermybatis: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 userWHERE 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.bioFROM user uLEFT JOIN profile p ON u.id = p.user_idWHERE 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_nameFROM department dLEFT JOIN employee e ON d.id = e.dept_idWHERE 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: trueaggressive-lazy-loading: false # false:仅加载被调用的属性
⚠️ 延迟加载会创建新会话,一级缓存失效。
✅ 方案三:使用 二级缓存
提升跨会话的数据复用性。
开启步骤:
- 全局开启缓存:
mybatis:configuration:cache-enabled: true
- 在 Mapper XML 中声明缓存:
<mapper namespace="com.example.mapper.UserMapper"><cache/> <!-- 默认使用 PerpetualCache --><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>
</mapper>
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 {@Autowiredprivate 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 {@Beanpublic 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 插件 |