MyBatis核心指南
# 一、概述
# 1. 什么是 MyBatis
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
与全自动 ORM 框架(如 Hibernate)不同,MyBatis 是一个半自动化的 ORM 框架,它将 SQL 语句的编写权交给开发者,提供了更大的灵活性和控制力。
# 2. MyBatis 的优势
- 简单易学:本身就很小且简单,没有任何第三方依赖,最简单安装只要两个 jar 文件+配置几个 SQL 映射文件
- 灵活性高:不会对应用程序或者数据库的现有设计强加任何影响,SQL 写在 XML 里,便于统一管理和优化
- SQL 可控:提供 XML 标签,支持编写动态 SQL 语句
- 解耦合:SQL 和代码的分离,提高了可维护性
- 提供映射标签:支持对象与数据库的 ORM 字段关系映射
- 提供对象关系映射标签:支持对象关系组建维护
- 提供 XML 标签:支持编写动态 SQL
# 3. MyBatis vs Hibernate vs JPA
| 特性 | MyBatis | Hibernate | JPA |
|---|---|---|---|
| 类型 | 半自动 ORM | 全自动 ORM | 规范/标准 |
| SQL 控制 | 完全控制 | 自动生成 | 自动生成 |
| 学习曲线 | 较低 | 较高 | 中等 |
| 性能优化 | 容易 | 较难 | 中等 |
| 移植性 | 较差 | 好 | 好 |
| 适用场景 | 复杂查询、性能要求高 | 快速开发、简单 CRUD | 标准化项目 |
# 二、快速入门
# 1. 添加依赖
# Maven 配置
<dependencies>
<!-- MyBatis 核心依赖 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.16</version>
</dependency>
<!-- MySQL 驱动 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.3.0</version>
</dependency>
</dependencies>
# Gradle 配置
dependencies {
implementation 'org.mybatis:mybatis:3.5.16'
implementation 'com.mysql:mysql-connector-j:8.3.0'
}
# 2. 核心配置文件
创建 mybatis-config.xml 配置文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 环境配置 -->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis_demo?useSSL=false&serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="password"/>
</dataSource>
</environment>
</environments>
<!-- 映射器配置 -->
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
</configuration>
# 3. 创建实体类
public class User {
private Long id;
private String username;
private String email;
private Integer age;
private LocalDateTime createTime;
// 构造方法
public User() {}
public User(String username, String email, Integer age) {
this.username = username;
this.email = email;
this.age = age;
}
// Getter 和 Setter 方法
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public Integer getAge() { return age; }
public void setAge(Integer age) { this.age = age; }
public LocalDateTime getCreateTime() { return createTime; }
public void setCreateTime(LocalDateTime createTime) { this.createTime = createTime; }
@Override
public String toString() {
return "User{id=" + id + ", username='" + username + "', email='" + email +
"', age=" + age + ", createTime=" + createTime + "}";
}
}
# 4. 创建 Mapper 接口
public interface UserMapper {
// 根据 ID 查询用户
User selectById(Long id);
// 查询所有用户
List<User> selectAll();
// 插入用户
int insert(User user);
// 更新用户
int update(User user);
// 删除用户
int deleteById(Long id);
}
# 5. 创建 Mapper XML 文件
创建 mapper/UserMapper.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.UserMapper">
<!-- 结果映射 -->
<resultMap id="UserResultMap" type="com.example.entity.User">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="email" column="email"/>
<result property="age" column="age"/>
<result property="createTime" column="create_time"/>
</resultMap>
<!-- 根据 ID 查询 -->
<select id="selectById" resultMap="UserResultMap">
SELECT id, username, email, age, create_time
FROM user
WHERE id = #{id}
</select>
<!-- 查询所有 -->
<select id="selectAll" resultMap="UserResultMap">
SELECT id, username, email, age, create_time
FROM user
</select>
<!-- 插入 -->
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
INSERT INTO user (username, email, age, create_time)
VALUES (#{username}, #{email}, #{age}, #{createTime})
</insert>
<!-- 更新 -->
<update id="update">
UPDATE user
SET username = #{username}, email = #{email}, age = #{age}
WHERE id = #{id}
</update>
<!-- 删除 -->
<delete id="deleteById">
DELETE FROM user WHERE id = #{id}
</delete>
</mapper>
# 6. 使用 MyBatis
public class MyBatisDemo {
public static void main(String[] args) throws IOException {
// 1. 读取配置文件
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// 2. 创建 SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 3. 获取 SqlSession(使用 try-with-resources 自动关闭)
try (SqlSession session = sqlSessionFactory.openSession()) {
// 4. 获取 Mapper 接口的代理对象
UserMapper mapper = session.getMapper(UserMapper.class);
// 5. 执行查询
User user = mapper.selectById(1L);
System.out.println(user);
// 6. 执行插入
User newUser = new User("张三", "zhangsan@example.com", 25);
mapper.insert(newUser);
// 7. 提交事务
session.commit();
}
}
}
# 三、核心配置详解
# 1. configuration 配置结构
MyBatis 的配置文件包含以下顶级元素(按顺序):
<configuration>
<properties/> <!-- 属性配置 -->
<settings/> <!-- 全局设置 -->
<typeAliases/> <!-- 类型别名 -->
<typeHandlers/> <!-- 类型处理器 -->
<objectFactory/> <!-- 对象工厂 -->
<plugins/> <!-- 插件 -->
<environments/> <!-- 环境配置 -->
<databaseIdProvider/> <!-- 数据库厂商标识 -->
<mappers/> <!-- 映射器 -->
</configuration>
# 2. properties 属性配置
可以通过外部属性文件或内部属性来配置:
<!-- 引入外部属性文件 -->
<properties resource="db.properties">
<!-- 也可以在这里定义属性 -->
<property name="username" value="root"/>
</properties>
<!-- 使用属性 -->
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
db.properties 文件:
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis_demo
username=root
password=password
# 3. settings 全局设置
常用的设置项:
<settings>
<!-- 开启驼峰命名自动映射 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- 开启延迟加载 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 开启二级缓存 -->
<setting name="cacheEnabled" value="true"/>
<!-- 指定日志实现 -->
<setting name="logImpl" value="SLF4J"/>
<!-- 允许 JDBC 支持自动生成主键 -->
<setting name="useGeneratedKeys" value="true"/>
<!-- 设置超时时间 -->
<setting name="defaultStatementTimeout" value="25"/>
<!-- 设置默认的执行器类型 -->
<setting name="defaultExecutorType" value="SIMPLE"/>
</settings>
# 4. typeAliases 类型别名
类型别名可以为 Java 类型设置一个缩写名字,减少类完全限定名的冗余:
<typeAliases>
<!-- 单个类型别名 -->
<typeAlias alias="User" type="com.example.entity.User"/>
<!-- 包扫描方式(推荐) -->
<package name="com.example.entity"/>
</typeAliases>
使用 @Alias 注解自定义别名:
@Alias("user")
public class User {
// ...
}
MyBatis 内置的常用类型别名:
| 别名 | 映射的类型 |
|---|---|
_int | int |
_long | long |
int | Integer |
long | Long |
string | String |
date | Date |
map | Map |
list | List |
# 5. environments 环境配置
MyBatis 支持配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境:
<environments default="development">
<!-- 开发环境 -->
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${dev.driver}"/>
<property name="url" value="${dev.url}"/>
<property name="username" value="${dev.username}"/>
<property name="password" value="${dev.password}"/>
</dataSource>
</environment>
<!-- 生产环境 -->
<environment id="production">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${prod.driver}"/>
<property name="url" value="${prod.url}"/>
<property name="username" value="${prod.username}"/>
<property name="password" value="${prod.password}"/>
</dataSource>
</environment>
</environments>
事务管理器类型:
JDBC:直接使用 JDBC 的提交和回滚功能MANAGED:让容器来管理事务的整个生命周期
数据源类型:
UNPOOLED:每次请求时打开和关闭连接POOLED:使用连接池(推荐)JNDI:使用 JNDI 数据源
# 6. mappers 映射器配置
四种配置方式:
<mappers>
<!-- 方式一:使用相对于类路径的资源引用 -->
<mapper resource="mapper/UserMapper.xml"/>
<!-- 方式二:使用完全限定资源定位符(URL) -->
<mapper url="file:///var/mappers/UserMapper.xml"/>
<!-- 方式三:使用映射器接口实现类的完全限定类名 -->
<mapper class="com.example.mapper.UserMapper"/>
<!-- 方式四:将包内的映射器接口全部注册(推荐) -->
<package name="com.example.mapper"/>
</mappers>
# 四、XML 映射文件详解
# 1. 参数传递
# 单个参数
<select id="selectById" resultType="User">
SELECT * FROM user WHERE id = #{id}
</select>
# 多个参数
方式一:使用 @Param 注解
User selectByUsernameAndAge(@Param("username") String username, @Param("age") Integer age);
<select id="selectByUsernameAndAge" resultType="User">
SELECT * FROM user WHERE username = #{username} AND age = #{age}
</select>
方式二:使用 Map
User selectByMap(Map<String, Object> params);
<select id="selectByMap" resultType="User">
SELECT * FROM user WHERE username = #{username} AND age = #{age}
</select>
方式三:使用实体对象
List<User> selectByCondition(User user);
<select id="selectByCondition" resultType="User">
SELECT * FROM user WHERE username = #{username} AND age = #{age}
</select>
# 2. #{} 与 ${} 的区别
| 特性 | #{} | ${} |
|---|---|---|
| 处理方式 | 预编译处理 | 字符串替换 |
| SQL 注入 | 安全,防止 SQL 注入 | 不安全,存在 SQL 注入风险 |
| 使用场景 | 参数值 | 表名、列名等动态 SQL 片段 |
<!-- 使用 #{} 传递参数值(推荐) -->
<select id="selectById" resultType="User">
SELECT * FROM user WHERE id = #{id}
</select>
<!-- 使用 ${} 动态表名(谨慎使用) -->
<select id="selectFromTable" resultType="User">
SELECT * FROM ${tableName} WHERE id = #{id}
</select>
# 3. resultMap 结果映射
# 基本映射
<resultMap id="UserResultMap" type="User">
<id property="id" column="id"/>
<result property="username" column="user_name"/>
<result property="email" column="email"/>
<result property="createTime" column="create_time"/>
</resultMap>
# 关联映射(一对一)
public class User {
private Long id;
private String username;
private UserDetail detail; // 一对一关联
}
<resultMap id="UserWithDetailMap" type="User">
<id property="id" column="id"/>
<result property="username" column="username"/>
<!-- 一对一关联 -->
<association property="detail" javaType="UserDetail">
<id property="id" column="detail_id"/>
<result property="address" column="address"/>
<result property="phone" column="phone"/>
</association>
</resultMap>
<select id="selectUserWithDetail" resultMap="UserWithDetailMap">
SELECT u.id, u.username, d.id as detail_id, d.address, d.phone
FROM user u
LEFT JOIN user_detail d ON u.id = d.user_id
WHERE u.id = #{id}
</select>
# 集合映射(一对多)
public class User {
private Long id;
private String username;
private List<Order> orders; // 一对多关联
}
<resultMap id="UserWithOrdersMap" type="User">
<id property="id" column="id"/>
<result property="username" column="username"/>
<!-- 一对多关联 -->
<collection property="orders" ofType="Order">
<id property="id" column="order_id"/>
<result property="orderNo" column="order_no"/>
<result property="amount" column="amount"/>
</collection>
</resultMap>
<select id="selectUserWithOrders" resultMap="UserWithOrdersMap">
SELECT u.id, u.username, o.id as order_id, o.order_no, o.amount
FROM user u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.id = #{id}
</select>
# 4. 自动映射
当开启 mapUnderscoreToCamelCase 设置后,MyBatis 会自动将数据库下划线命名映射到 Java 驼峰命名:
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
这样 create_time 会自动映射到 createTime。
# 五、动态 SQL
动态 SQL 是 MyBatis 的强大特性之一,它允许根据不同条件动态生成 SQL 语句。
# 1. if 条件判断
<select id="selectByCondition" resultType="User">
SELECT * FROM user
WHERE 1=1
<if test="username != null and username != ''">
AND username LIKE CONCAT('%', #{username}, '%')
</if>
<if test="email != null and email != ''">
AND email = #{email}
</if>
<if test="age != null">
AND age = #{age}
</if>
</select>
# 2. choose/when/otherwise
类似于 Java 的 switch 语句:
<select id="selectByPriority" resultType="User">
SELECT * FROM user
WHERE 1=1
<choose>
<when test="id != null">
AND id = #{id}
</when>
<when test="username != null">
AND username = #{username}
</when>
<otherwise>
AND status = 'ACTIVE'
</otherwise>
</choose>
</select>
# 3. where 标签
where 标签会自动处理 WHERE 关键字和多余的 AND/OR:
<select id="selectByCondition" resultType="User">
SELECT * FROM user
<where>
<if test="username != null and username != ''">
AND username LIKE CONCAT('%', #{username}, '%')
</if>
<if test="email != null">
AND email = #{email}
</if>
<if test="age != null">
AND age = #{age}
</if>
</where>
</select>
# 4. set 标签
set 标签用于动态更新语句,会自动处理多余的逗号:
<update id="updateSelective">
UPDATE user
<set>
<if test="username != null">username = #{username},</if>
<if test="email != null">email = #{email},</if>
<if test="age != null">age = #{age},</if>
</set>
WHERE id = #{id}
</update>
# 5. trim 标签
trim 标签是 where 和 set 的通用版本:
<!-- 等价于 where 标签 -->
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>
<!-- 等价于 set 标签 -->
<trim prefix="SET" suffixOverrides=",">
...
</trim>
# 6. foreach 遍历
用于遍历集合,常用于 IN 查询和批量操作:
<!-- IN 查询 -->
<select id="selectByIds" resultType="User">
SELECT * FROM user
WHERE id IN
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</select>
<!-- 批量插入 -->
<insert id="batchInsert">
INSERT INTO user (username, email, age) VALUES
<foreach collection="users" item="user" separator=",">
(#{user.username}, #{user.email}, #{user.age})
</foreach>
</insert>
foreach 属性说明:
collection:要遍历的集合(List 用list,数组用array,Map 用map,或使用@Param指定的名称)item:当前遍历元素的变量名index:当前遍历的索引open:开始符号close:结束符号separator:分隔符
# 7. sql 片段复用
使用 sql 标签定义可复用的 SQL 片段:
<!-- 定义 SQL 片段 -->
<sql id="userColumns">
id, username, email, age, create_time
</sql>
<sql id="whereCondition">
<where>
<if test="username != null">AND username = #{username}</if>
<if test="email != null">AND email = #{email}</if>
</where>
</sql>
<!-- 引用 SQL 片段 -->
<select id="selectAll" resultType="User">
SELECT <include refid="userColumns"/>
FROM user
<include refid="whereCondition"/>
</select>
# 六、注解开发
除了 XML 配置,MyBatis 也支持使用注解进行开发,适合简单的 SQL 操作。
# 1. 基本 CRUD 注解
public interface UserMapper {
@Select("SELECT * FROM user WHERE id = #{id}")
User selectById(Long id);
@Select("SELECT * FROM user")
List<User> selectAll();
@Insert("INSERT INTO user(username, email, age) VALUES(#{username}, #{email}, #{age})")
@Options(useGeneratedKeys = true, keyProperty = "id")
int insert(User user);
@Update("UPDATE user SET username=#{username}, email=#{email} WHERE id=#{id}")
int update(User user);
@Delete("DELETE FROM user WHERE id = #{id}")
int deleteById(Long id);
}
# 2. @Results 结果映射
@Results(id = "userResultMap", value = {
@Result(property = "id", column = "id", id = true),
@Result(property = "username", column = "user_name"),
@Result(property = "email", column = "email"),
@Result(property = "createTime", column = "create_time")
})
@Select("SELECT * FROM user WHERE id = #{id}")
User selectById(Long id);
// 复用结果映射
@ResultMap("userResultMap")
@Select("SELECT * FROM user")
List<User> selectAll();
# 3. @Provider 动态 SQL
对于复杂的动态 SQL,可以使用 Provider 注解:
public interface UserMapper {
@SelectProvider(type = UserSqlProvider.class, method = "selectByCondition")
List<User> selectByCondition(User user);
@InsertProvider(type = UserSqlProvider.class, method = "insertSelective")
int insertSelective(User user);
@UpdateProvider(type = UserSqlProvider.class, method = "updateSelective")
int updateSelective(User user);
}
public class UserSqlProvider {
public String selectByCondition(User user) {
return new SQL() {{
SELECT("*");
FROM("user");
if (user.getUsername() != null) {
WHERE("username = #{username}");
}
if (user.getEmail() != null) {
WHERE("email = #{email}");
}
if (user.getAge() != null) {
WHERE("age = #{age}");
}
}}.toString();
}
public String insertSelective(User user) {
return new SQL() {{
INSERT_INTO("user");
if (user.getUsername() != null) {
VALUES("username", "#{username}");
}
if (user.getEmail() != null) {
VALUES("email", "#{email}");
}
if (user.getAge() != null) {
VALUES("age", "#{age}");
}
}}.toString();
}
public String updateSelective(User user) {
return new SQL() {{
UPDATE("user");
if (user.getUsername() != null) {
SET("username = #{username}");
}
if (user.getEmail() != null) {
SET("email = #{email}");
}
if (user.getAge() != null) {
SET("age = #{age}");
}
WHERE("id = #{id}");
}}.toString();
}
}
# 4. 注解与 XML 混合使用
可以在同一个 Mapper 中混合使用注解和 XML:
public interface UserMapper {
// 简单查询使用注解
@Select("SELECT * FROM user WHERE id = #{id}")
User selectById(Long id);
// 复杂查询在 XML 中定义
List<User> selectByComplexCondition(Map<String, Object> params);
}
# 七、缓存机制
# 1. 一级缓存(本地缓存)
一级缓存是 SqlSession 级别的缓存,默认开启,同一个 SqlSession 中执行相同的查询会直接从缓存获取。
try (SqlSession session = sqlSessionFactory.openSession()) {
UserMapper mapper = session.getMapper(UserMapper.class);
// 第一次查询,执行 SQL
User user1 = mapper.selectById(1L);
// 第二次查询,从一级缓存获取,不执行 SQL
User user2 = mapper.selectById(1L);
System.out.println(user1 == user2); // true
}
一级缓存失效的情况:
- 不同的
SqlSession - 同一个
SqlSession,但查询条件不同 - 同一个
SqlSession,两次查询之间执行了增删改操作 - 同一个
SqlSession,手动清空了缓存(session.clearCache())
# 2. 二级缓存(全局缓存)
二级缓存是 namespace 级别的缓存,可以跨 SqlSession 共享。
开启二级缓存:
- 在
mybatis-config.xml中开启全局缓存(默认已开启):
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
- 在 Mapper XML 中添加
<cache/>标签:
<mapper namespace="com.example.mapper.UserMapper">
<!-- 开启二级缓存 -->
<cache
eviction="LRU"
flushInterval="60000"
size="512"
readOnly="true"/>
<!-- ... -->
</mapper>
cache 属性说明:
eviction:缓存回收策略LRU(默认):最近最少使用FIFO:先进先出SOFT:软引用WEAK:弱引用
flushInterval:刷新间隔(毫秒)size:缓存对象数量readOnly:是否只读
- 实体类需要实现
Serializable接口:
public class User implements Serializable {
private static final long serialVersionUID = 1L;
// ...
}
# 3. 自定义缓存
可以集成第三方缓存(如 Redis、Ehcache):
<cache type="org.mybatis.caches.redis.RedisCache"/>
注:企业开发中一般不在ORM层做缓存,而是在业务层做缓存。参考:高性能-缓存架构设计
# 八、Spring Boot 集成
# 1. 添加依赖
<dependencies>
<!-- MyBatis Spring Boot Starter -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
<!-- MySQL 驱动 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
# 2. 配置文件
application.yml:
spring:
datasource:
url: jdbc:mysql://localhost:3306/mybatis_demo?useSSL=false&serverTimezone=UTC
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
# Mapper XML 文件位置
mapper-locations: classpath:mapper/*.xml
# 实体类包路径(用于类型别名)
type-aliases-package: com.example.entity
configuration:
# 开启驼峰命名映射
map-underscore-to-camel-case: true
# 开启二级缓存
cache-enabled: true
# 日志实现
log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl
# 3. 启动类配置
@SpringBootApplication
@MapperScan("com.example.mapper") // 扫描 Mapper 接口
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
# 4. 使用示例
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public User getUserById(Long id) {
return userMapper.selectById(id);
}
@Transactional
public void createUser(User user) {
userMapper.insert(user);
}
}
# 九、MyBatis-Plus 简介
MyBatis-Plus(简称 MP)是 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
# 1. 核心特性
- 无侵入:只做增强不做改变,引入它不会对现有工程产生影响
- 损耗小:启动即会自动注入基本 CRUD,性能基本无损耗
- 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作
- 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件
- 支持主键自动生成:支持多达 4 种主键策略
- 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper、Model、Service、Controller 层代码
- 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作
- 内置性能分析插件:可输出 SQL 语句以及其执行时间
# 2. 快速开始
添加依赖:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.9</version>
</dependency>
实体类:
@Data
@TableName("user")
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String username;
private String email;
private Integer age;
@TableField("create_time")
private LocalDateTime createTime;
}
Mapper 接口:
@Mapper
public interface UserMapper extends BaseMapper<User> {
// 继承 BaseMapper 后,自动拥有基本的 CRUD 方法
}
使用示例:
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
// 基本 CRUD
public User getById(Long id) {
return userMapper.selectById(id);
}
public List<User> getAll() {
return userMapper.selectList(null);
}
public void save(User user) {
userMapper.insert(user);
}
// 条件构造器
public List<User> getByCondition(String username, Integer minAge) {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.like(StringUtils.isNotBlank(username), User::getUsername, username)
.ge(minAge != null, User::getAge, minAge)
.orderByDesc(User::getCreateTime);
return userMapper.selectList(wrapper);
}
// 分页查询
public IPage<User> getPage(int pageNum, int pageSize) {
Page<User> page = new Page<>(pageNum, pageSize);
return userMapper.selectPage(page, null);
}
}
# 十、最佳实践
# 1. SQL 优化建议
- **避免 SELECT ***:明确指定需要的列,减少数据传输
- 合理使用索引:在 WHERE、ORDER BY、JOIN 条件中的列上建立索引
- 分页查询:大数据量查询时使用分页,避免一次性加载过多数据
- 批量操作:使用批量插入/更新代替循环单条操作
<!-- 批量插入示例 -->
<insert id="batchInsert">
INSERT INTO user (username, email, age) VALUES
<foreach collection="list" item="user" separator=",">
(#{user.username}, #{user.email}, #{user.age})
</foreach>
</insert>
# 2. 防止 SQL 注入
- 优先使用
#{}而不是${} - 对于必须使用
${}的场景(如动态表名),进行白名单校验
// 白名单校验示例
public List<User> selectFromTable(String tableName) {
Set<String> allowedTables = Set.of("user", "admin", "guest");
if (!allowedTables.contains(tableName)) {
throw new IllegalArgumentException("Invalid table name");
}
return userMapper.selectFromTable(tableName);
}
# 3. 日志配置
开发环境建议开启 SQL 日志,便于调试:
# application.yml
logging:
level:
com.example.mapper: debug
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 4. 事务管理
在 Spring 环境中使用 @Transactional 注解管理事务:
@Service
public class UserService {
@Transactional(rollbackFor = Exception.class)
public void transferBalance(Long fromId, Long toId, BigDecimal amount) {
// 扣减余额
userMapper.decreaseBalance(fromId, amount);
// 增加余额
userMapper.increaseBalance(toId, amount);
}
}
# 5. 常见问题排查
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| Mapper 接口找不到 | 未配置 @MapperScan 或包路径错误 | 检查启动类的 @MapperScan 配置 |
| 参数绑定失败 | 多参数未使用 @Param | 添加 @Param 注解 |
| 结果映射为 null | 列名与属性名不匹配 | 使用 resultMap 或开启驼峰映射 |
| 缓存不生效 | 未开启二级缓存或实体未序列化 | 检查缓存配置和 Serializable 接口 |
# 十一、总结
MyBatis 作为一款优秀的持久层框架,以其灵活性和对 SQL 的完全控制而广受欢迎。本文介绍了 MyBatis 的核心概念和使用方法:
- 基础配置:了解 MyBatis 的核心配置文件结构和各项配置的作用
- CRUD 操作:掌握基本的增删改查操作和参数传递方式
- 结果映射:学会使用
resultMap处理复杂的对象关系映射 - 动态 SQL:灵活运用
if、choose、where、foreach等标签构建动态查询 - 注解开发:了解注解方式的开发模式,适用于简单场景
- 缓存机制:理解一级缓存和二级缓存的工作原理
- Spring Boot 集成:掌握在 Spring Boot 项目中使用 MyBatis 的方法
- MyBatis-Plus:了解增强工具的使用,提高开发效率
在实际项目中,建议根据业务复杂度选择合适的使用方式:简单 CRUD 可以使用注解或 MyBatis-Plus,复杂查询则使用 XML 配置。同时注意 SQL 优化和安全性,编写高质量的持久层代码。
祝你变得更强!