一、初识 MyBatis:为什么它如此受欢迎?
在 Java 持久层框架的版图中,MyBatis 无疑占据着举足轻重的地位。它是一个优秀的半自动 ORM(对象关系映射)框架,能够将 Java 对象与数据库记录进行映射,支持自定义 SQL、存储过程以及高级映射。相比于 Hibernate 的全自动 ORM,MyBatis 允许开发者直接编写 SQL,在需要精细控制 SQL 执行和复杂查询的场景中,展现出无可比拟的灵活性和性能优势。
简单来说,MyBatis 的核心工作就是 将 SQL 语句与 Java 代码解耦,通过 XML 或注解的配置方式,让你既能享受框架带来的便捷,又能牢牢掌控 SQL 的每一个细节。正因如此,MyBatis 在国内的 Java 开发社区中堪称"半壁江山",几乎是企业级项目数据访问层的标准配置。
1.1 MyBatis 的核心优势
SQL 与代码解耦:MyBatis 通过 XML 或注解方式将 SQL 语句与 Java 代码分离,开发者可独立维护数据库操作逻辑。DBA 可以直接优化 SQL 而无需修改业务代码,这在需要精细控制 SQL 的遗留系统迁移场景中尤为珍贵。
动态 SQL 灵活构建 :MyBatis 提供 <if>、<choose>、<foreach> 等标签实现条件拼接,相比于 JPA 的 Criteria API,MyBatis 的动态 SQL 更接近原生 SQL 语法,调试成本可降低 60% 以上。
性能优化空间巨大 :一级缓存默认开启,可减少重复查询,缓存命中率可达 35%;延迟加载通过 fetchType="lazy" 实现关联对象按需加载,避免 N+1 查询问题;批处理模式下,1000 条插入操作可从 10 秒压缩至 2 秒。
数据库兼容性强 :支持 Oracle、MySQL、PostgreSQL 等 12 种主流数据库,通过 typeAlias 和 typeHandlers 可无缝适配特殊数据类型。
1.2 MyBatis 的局限性
当然,任何框架都不是万能的。当实体类包含 50+ 字段时,XML 映射文件容易变得臃肿(单个实体映射文件超过 800 行时,字段修改导致的维护错误率可能上升 40%)。此外,分页需要手动处理,相比 MyBatis-Plus 的自动分页略显繁琐。
二、MyBatis 核心架构解析
2.1 核心组件
MyBatis 的核心组件如同一个精密的机器,各司其职:
| 组件 | 作用 |
|---|---|
SqlSessionFactoryBuilder |
使用建造者模式,解析配置文件并创建 SqlSessionFactory |
SqlSessionFactory |
工厂类,用于创建 SqlSession,应设计为单例 |
SqlSession |
代表一次数据库会话,提供增删改查 API,线程不安全 |
Configuration |
存储全局配置信息、映射信息、类型处理器,是 MyBatis 的"大脑" |
Executor |
执行器,负责执行 SQL,维护一级缓存 |
StatementHandler |
封装 JDBC Statement 的操作 |
ParameterHandler |
负责处理 SQL 参数 |
ResultSetHandler |
负责结果集的映射 |
2.2 一条 SQL 的执行全流程
以执行一次 userMapper.selectUserById(1) 为例,背后的故事是这样的:
1. 初始化
├─ SqlSessionFactoryBuilder → 构建 SqlSessionFactory
└─ SqlSessionFactory → 解析 XML/注解,构建 Configuration
2. 获取 SqlSession
└─ SqlSessionFactory.openSession() → 创建 DefaultSqlSession
3. Mapper 动态代理
└─ UserMapper mapper = sqlSession.getMapper(UserMapper.class)(生成 MapperProxy 代理对象)
4. 执行 SQL
├─ mapper.selectUserById(1)
├─ MapperProxy.invoke() → 根据方法找到 MappedStatement
└─ SqlSession.selectOne(mappedStatement, params)
5. Executor 执行
├─ CachingExecutor (二级缓存) → delegate → SimpleExecutor
├─ 创建 StatementHandler
├─ ParameterHandler 设置参数
├─ JDBC PreparedStatement 执行 SQL
└─ ResultSetHandler 映射结果
6. 返回结果
整个流程的核心在于:配置 → SqlSessionFactory → SqlSession → Executor → MappedStatement → 数据库。
2.3 MappedStatement 的秘密
MappedStatement 是 MyBatis 中一个关键的底层封装对象,它包装了 MyBatis 配置信息及 SQL 映射信息。mapper.xml 文件中的一个 SQL 对应一个 MappedStatement 对象,SQL 的 id 就是 MappedStatement 的 id。在 SQL 执行前,Executor 通过 MappedStatement 将输入的 Java 对象映射至 SQL 中;执行后,再将输出结果映射至 Java 对象中。
三、Spring Boot 整合 MyBatis(实战篇)
3.1 依赖导入
在 Spring Boot 项目中整合 MyBatis,首先需要在 pom.xml 中添加相关依赖:
xml
<!-- MyBatis Spring Boot Starter -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.3.2</version>
</dependency>
<!-- MySQL 驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 可选:连接池(HikariCP 已默认包含) -->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</dependency>
💡 小贴士 :
mybatis-spring-boot-starter会自动引入mybatis和mybatis-spring依赖,我们只需添加这一个 starter 即可。
3.2 配置文件详解(application.yml)
yaml
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://localhost:3306/mybatis_demo?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&serverTimezone=Asia/Shanghai
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
maximum-pool-size: 10 # 最大连接池数
minimum-idle: 5 # 最小空闲连接
connection-timeout: 30000 # 连接超时时间
idle-timeout: 600000 # 空闲超时时间
max-lifetime: 1800000 # 连接最大生命周期
mybatis:
# 实体类包路径,用于别名自动注册
type-aliases-package: com.example.demo.entity
# Mapper XML 文件位置
mapper-locations: classpath:mapper/*.xml
configuration:
# 开启驼峰命名自动映射(例如:user_name → userName)
map-underscore-to-camel-case: true
# 开启二级缓存
cache-enabled: true
# 开启延迟加载
lazy-loading-enabled: true
# 日志输出
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
关键配置解读:
map-underscore-to-camel-case: true:这个配置非常实用!开启后,数据库字段user_name会自动映射到 Java 实体类的userName属性,省去了大量手动映射的烦恼。type-aliases-package:指定实体类包路径后,在 Mapper XML 中就可以直接使用类名(小写形式)而非全限定名。mapper-locations:指定 XML 映射文件的存放位置。
3.3 基于 XML 的开发方式
这是 MyBatis 最经典的开发模式,适合复杂的 SQL 映射场景。
Step 1:创建实体类
java
package com.example.demo.entity;
import lombok.Data;
import java.util.Date;
@Data
public class User {
private Long id;
private String userName;
private String password;
private String email;
private Integer age;
private Integer status;
private Date createTime;
private Date updateTime;
}
Step 2:创建 Mapper 接口
java
package com.example.demo.mapper;
import com.example.demo.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface UserMapper {
User selectUserById(@Param("id") Long id);
List<User> selectUserList(@Param("userName") String userName);
int insertUser(User user);
int updateUser(User user);
int deleteUserById(@Param("id") Long id);
}
Step 3:编写 Mapper XML 文件
在 src/main/resources/mapper/ 目录下创建 UserMapper.xml:
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">
<!-- 结果映射 -->
<resultMap id="BaseResultMap" type="User">
<id column="id" property="id" jdbcType="BIGINT"/>
<result column="user_name" property="userName" jdbcType="VARCHAR"/>
<result column="password" property="password" jdbcType="VARCHAR"/>
<result column="email" property="email" jdbcType="VARCHAR"/>
<result column="age" property="age" jdbcType="INTEGER"/>
<result column="status" property="status" jdbcType="INTEGER"/>
<result column="create_time" property="createTime" jdbcType="TIMESTAMP"/>
<result column="update_time" property="updateTime" jdbcType="TIMESTAMP"/>
</resultMap>
<!-- 基础字段列表 -->
<sql id="Base_Column_List">
id, user_name, password, email, age, status, create_time, update_time
</sql>
<!-- 根据 ID 查询用户 -->
<select id="selectUserById" resultMap="BaseResultMap">
SELECT <include refid="Base_Column_List"/>
FROM user
WHERE id = #{id}
</select>
<!-- 条件查询用户列表 -->
<select id="selectUserList" resultMap="BaseResultMap">
SELECT <include refid="Base_Column_List"/>
FROM user
WHERE 1=1
<if test="userName != null and userName != ''">
AND user_name LIKE CONCAT('%', #{userName}, '%')
</if>
ORDER BY create_time DESC
</select>
<!-- 插入用户 -->
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
INSERT INTO user (user_name, password, email, age, status, create_time, update_time)
VALUES (#{userName}, #{password}, #{email}, #{age}, #{status}, NOW(), NOW())
</insert>
<!-- 更新用户 -->
<update id="updateUser">
UPDATE user
<set>
<if test="userName != null">user_name = #{userName},</if>
<if test="password != null">password = #{password},</if>
<if test="email != null">email = #{email},</if>
<if test="age != null">age = #{age},</if>
<if test="status != null">status = #{status},</if>
update_time = NOW()
</set>
WHERE id = #{id}
</update>
<!-- 删除用户 -->
<delete id="deleteUserById">
DELETE FROM user WHERE id = #{id}
</delete>
</mapper>
Step 4:创建 Service 层
java
package com.example.demo.service;
import com.example.demo.entity.User;
import java.util.List;
public interface UserService {
User getUserById(Long id);
List<User> getUserList(String userName);
int addUser(User user);
int modifyUser(User user);
int removeUser(Long id);
}
java
package com.example.demo.service.impl;
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
@Transactional
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public User getUserById(Long id) {
return userMapper.selectUserById(id);
}
@Override
public List<User> getUserList(String userName) {
return userMapper.selectUserList(userName);
}
@Override
public int addUser(User user) {
return userMapper.insertUser(user);
}
@Override
public int modifyUser(User user) {
return userMapper.updateUser(user);
}
@Override
public int removeUser(Long id) {
return userMapper.deleteUserById(id);
}
}
Step 5:Controller 层测试
java
package com.example.demo.controller;
import com.example.demo.entity.User;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/{id}")
public User getUserById(@PathVariable Long id) {
return userService.getUserById(id);
}
@GetMapping("/list")
public List<User> getUserList(@RequestParam(required = false) String userName) {
return userService.getUserList(userName);
}
@PostMapping
public int addUser(@RequestBody User user) {
return userService.addUser(user);
}
@PutMapping
public int updateUser(@RequestBody User user) {
return userService.modifyUser(user);
}
@DeleteMapping("/{id}")
public int deleteUser(@PathVariable Long id) {
return userService.removeUser(id);
}
}
3.4 基于注解的开发方式
对于简单的 CRUD 操作,注解方式更加简洁高效:
java
package com.example.demo.mapper;
import com.example.demo.entity.User;
import org.apache.ibatis.annotations.*;
import java.util.List;
@Mapper
public interface UserAnnotationMapper {
@Select("SELECT id, user_name, password, email, age, status, create_time, update_time FROM user WHERE id = #{id}")
@Results(id = "userResultMap", value = {
@Result(column = "id", property = "id", id = true),
@Result(column = "user_name", property = "userName"),
@Result(column = "create_time", property = "createTime"),
@Result(column = "update_time", property = "updateTime")
})
User selectUserById(@Param("id") Long id);
@SelectProvider(type = UserSqlProvider.class, method = "selectUserList")
@ResultMap("userResultMap")
List<User> selectUserList(@Param("userName") String userName);
@Insert("INSERT INTO user (user_name, password, email, age, status, create_time, update_time) " +
"VALUES (#{userName}, #{password}, #{email}, #{age}, #{status}, NOW(), NOW())")
@Options(useGeneratedKeys = true, keyProperty = "id")
int insertUser(User user);
@Update("UPDATE user SET user_name = #{userName}, password = #{password}, email = #{email}, " +
"age = #{age}, status = #{status}, update_time = NOW() WHERE id = #{id}")
int updateUser(User user);
@Delete("DELETE FROM user WHERE id = #{id}")
int deleteUserById(@Param("id") Long id);
}
对于复杂查询,可以使用 @SelectProvider 配合 SQL 构建器实现动态 SQL:
java
package com.example.demo.mapper;
import org.apache.ibatis.jdbc.SQL;
public class UserSqlProvider {
public String selectUserList(String userName) {
return new SQL() {{
SELECT("id, user_name, password, email, age, status, create_time, update_time");
FROM("user");
if (userName != null && !userName.isEmpty()) {
WHERE("user_name LIKE CONCAT('%', #{userName}, '%')");
}
ORDER_BY("create_time DESC");
}}.toString();
}
}
3.5 Mapper 扫描配置
除了在每一个 Mapper 接口上添加 @Mapper 注解,还可以通过 @MapperScan 在配置类中批量扫描:
java
package com.example.demo.config;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@MapperScan("com.example.demo.mapper") // 扫描整个 mapper 包
public class MyBatisConfig {
}
四、MyBatis 核心功能深入
4.1 参数传递方式
在 MyBatis 中,Mapper 接口方法支持多种参数传递方式:
方式一:使用 @Param 注解(推荐)
java
User selectUser(@Param("id") Long id, @Param("status") Integer status);
xml
<select id="selectUser" resultType="User">
SELECT * FROM user WHERE id = #{id} AND status = #{status}
</select>
方式二:使用对象参数
java
int insertUser(User user);
xml
<insert id="insertUser">
INSERT INTO user (user_name, password) VALUES (#{userName}, #{password})
</insert>
方式三:使用 Map 参数
java
List<User> selectUserByMap(Map<String, Object> params);
4.2 结果集映射
MyBatis 提供了两种结果集映射方式:
简单映射(resultType) :当查询结果的列名与 Java 对象属性名一致(或开启驼峰映射后能匹配)时使用。
复杂映射(resultMap) :支持一对一、一对多等复杂关系映射:
- 一对一(association) :适用于主表与从表是一对一关系(如:用户和用户详情)
- 一对多(collection) :适用于主表与从表是一对多关系(如:用户和订单列表)
xml
<!-- 一对一关联映射 -->
<resultMap id="UserWithDetailResultMap" type="User" extends="BaseResultMap">
<association property="userDetail" javaType="UserDetail" column="id"
select="com.example.demo.mapper.UserDetailMapper.selectByUserId"/>
</resultMap>
<!-- 一对多关联映射 -->
<resultMap id="UserWithOrdersResultMap" type="User" extends="BaseResultMap">
<collection property="orderList" ofType="Order" column="id"
select="com.example.demo.mapper.OrderMapper.selectByUserId"/>
</resultMap>
💡 性能提示 :对于关联映射,建议开启延迟加载(
lazyLoadingEnabled=true),避免 N+1 查询问题。
4.3 动态 SQL(核心精华)
动态 SQL 是 MyBatis 最强大的特性之一,允许根据运行时条件动态生成 SQL 语句。
常用动态 SQL 标签详解
| 标签 | 作用 | 示例 |
|---|---|---|
<if> |
条件判断,满足条件时包含 SQL 片段 | <if test="name != null">AND name = #{name}</if> |
<choose> |
多条件分支(类似 switch-case) | <choose><when test="age != null">AND age = #{age}</when><otherwise>AND age > 18</otherwise></choose> |
<where> |
智能处理 WHERE 子句,自动去除开头 AND/OR | <where><if test="name != null">AND name = #{name}</if></where> |
<set> |
智能处理 UPDATE 语句,自动去除结尾逗号 | <set><if test="name != null">name = #{name},</if></set> |
<foreach> |
遍历集合生成批量操作 | <foreach item="id" collection="ids" open="(" separator="," close=")">#{id}</foreach> |
<trim> |
自定义字符串修剪 | <trim prefix="WHERE" prefixOverrides="AND">...</trim> |
<bind> |
创建变量并绑定到上下文 | <bind name="pattern" value="'%' + keyword + '%'" /> |
批量插入示例
xml
<insert id="batchInsertUsers">
INSERT INTO user (user_name, password, email) VALUES
<foreach collection="list" item="user" separator=",">
(#{user.userName}, #{user.password}, #{user.email})
</foreach>
</insert>
批量删除示例
xml
<delete id="batchDeleteUsers">
DELETE FROM user WHERE id IN
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
动态 SQL 最佳实践 :避免嵌套超过 3 层,使用 <where>、<set> 减少无效字符处理开销,参数合法性在 Java 层校验,简单场景可用 @SelectProvider 替代 XML。
4.4 缓存机制
MyBatis 提供了两级缓存来提升查询性能:
一级缓存(Local Cache)
- 作用域:SqlSession 级别,默认开启且无法关闭
- 生命周期 :与 SqlSession 一致,执行 INSERT/UPDATE/DELETE 操作或调用
commit()、close()时会清空 - 适用场景:同一会话内的重复查询,缓存命中率约 5%
二级缓存(Global Cache)
- 作用域:Mapper 级别(跨 SqlSession 共享),需手动配置开启
- 存储介质:默认 JVM 内存,可集成 Redis、Ehcache 等
- 适用场景:多用户并发访问、查询频率高且更新不频繁的数据,缓存命中率可达 90%
启用二级缓存配置:
yaml
# application.yml
mybatis:
configuration:
cache-enabled: true # 全局开启二级缓存
xml
<!-- Mapper XML 中添加 cache 标签 -->
<mapper namespace="com.example.demo.mapper.UserMapper">
<cache eviction="LRU" flushInterval="60000" size="512" readOnly="false"/>
</mapper>
java
// 或者使用注解方式
@CacheNamespace(eviction = LruCache.class, flushInterval = 60000, size = 512)
public interface UserMapper {
}
五、分页方案
5.1 PageHelper 插件(推荐)
PageHelper 是目前最流行的 MyBatis 分页插件,只需在查询前调用 PageHelper.startPage() 即可自动实现物理分页。
添加依赖:
xml
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
使用示例:
java
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
public PageInfo<User> getUserListByPage(int pageNum, int pageSize, String userName) {
// 开启分页(紧跟在后面的第一个查询会被分页)
PageHelper.startPage(pageNum, pageSize);
// 执行查询
List<User> userList = userMapper.selectUserList(userName);
// 封装分页结果
return new PageInfo<>(userList);
}
}
Controller 调用:
java
@GetMapping("/page")
public PageInfo<User> getUserByPage(@RequestParam(defaultValue = "1") int pageNum,
@RequestParam(defaultValue = "10") int pageSize,
@RequestParam(required = false) String userName) {
return userService.getUserListByPage(pageNum, pageSize, userName);
}
5.2 分页实现方式对比
MyBatis 主要有五种分页实现方式:
| 方式 | 类型 | 原理 | 适用场景 | 性能 |
|---|---|---|---|---|
| RowBounds | 逻辑分页 | 查询全部结果,内存中截取 | 小数据量 | 大数据量易内存溢出 |
| PageHelper | 物理分页 | 拦截器动态添加 LIMIT 子句 | 通用推荐 | 优秀 |
| SQL 分页 | 物理分页 | 手动编写 LIMIT/OFFSET | 简单场景 | 较好 |
| 数组分页 | 逻辑分页 | 查全表后 subList 截取 | 小数据量 | 差 |
| 拦截器分页 | 物理分页 | 自定义拦截器统一处理 | 需要统一分页规范时 | 优秀 |
💡 最佳实践 :推荐优先使用 PageHelper 等物理分页方式,避免深分页陷阱。对于超大数据量场景,建议设置
count=false跳过统计总记录数以提升性能。
六、性能优化指南
6.1 SQL 语句优化
- 精准查询字段 :避免使用
SELECT *,只查询业务所需字段 - 使用
<sql>标签:定义可复用字段集合,减少重复书写 - 批量操作合并 :使用
<foreach>生成批量插入/更新语句,减少数据库交互次数 - 避免 N+1 查询:合理使用延迟加载或联表查询
6.2 缓存策略优化
- 一级缓存 :默认开启,需配合
@Transactional注解生效,适用于单事务内重复查询场景 - 二级缓存:需手动配置开启,适合多用户并发访问场景。注意:不推荐跨 Mapper 共享热点数据,易引发缓存雪崩
- 分布式缓存 :在二级缓存基础上集成 Redis,通过
@Cacheable注解实现跨服务缓存一致性
6.3 连接池与监控
- 连接池推荐 :HikariCP(Spring Boot 默认)或 Druid,根据业务负载调整
maximum-pool-size、connection-timeout等参数 - 监控工具:通过 Druid 监控 SQL 执行时间,识别慢查询
- 日志控制 :生产环境关闭 MyBatis 详细日志(
log4j.logger.org.mybatis=ERROR),减少 IO 开销
七、多数据源配置
在微服务或复杂业务场景中,多数据源的需求十分常见。推荐使用 dynamic-datasource-spring-boot-starter 开源组件来优雅地实现。
7.1 添加依赖
xml
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>4.3.0</version>
</dependency>
7.2 配置文件
yaml
spring:
datasource:
dynamic:
primary: master # 默认数据源
strict: false # 严格模式(匹配不到时抛异常)
datasource:
master:
url: jdbc:mysql://localhost:3306/master_db
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
slave:
url: jdbc:mysql://localhost:3306/slave_db
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
7.3 使用 @DS 注解切换数据源
java
@Service
@DS("master") // 类级别指定默认数据源
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
@DS("slave") // 方法级别覆盖,从从库读取
public User getUserById(Long id) {
return userMapper.selectUserById(id);
}
@Override
@DS("master") // 写入操作使用主库
public int addUser(User user) {
return userMapper.insertUser(user);
}
}
💡 注解优先顺序:方法级别 > 类级别,
dynamic-datasource还支持读写分离、敏感信息加密、分布式事务(基于 Seata)等高级特性。
八、MyBatis-Plus:MyBatis 的超强增强工具
MyBatis-Plus(简称 MP)是 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,极大简化了单表 CRUD 操作。
8.1 核心特性
- 无侵入:只做增强,不改动 MyBatis 原有逻辑
- 内置通用 Mapper :提供
BaseMapper接口,包含常用 CRUD 方法,无需编写 XML - 条件构造器 :提供
LambdaQueryWrapper,类型安全的条件构造体验 - 代码生成器:自动生成 Entity、Mapper、Service、Controller 全套代码
- 内置分页插件:基于物理分页,支持多种数据库
8.2 代码生成器
MyBatis-Plus 的代码生成器 AutoGenerator 可以根据数据库表结构自动生成整套代码,极大提升开发效率。
添加依赖:
xml
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.3</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.31</version>
</dependency>
编写代码生成器主类:
java
public class CodeGenerator {
public static void main(String[] args) {
FastAutoGenerator.create("jdbc:mysql://localhost:3306/mybatis_demo", "root", "123456")
.globalConfig(builder -> {
builder.author("your_name")
.outputDir(System.getProperty("user.dir") + "/src/main/java")
.commentDate("yyyy/MM/dd");
})
.packageConfig(builder -> {
builder.parent("com.example.demo")
.entity("entity")
.mapper("mapper")
.service("service")
.serviceImpl("service.impl")
.controller("controller");
})
.strategyConfig(builder -> {
builder.addInclude("user") // 指定要生成的表名
.entityBuilder()
.enableLombok() // 开启 Lombok 支持
.idType(IdType.AUTO); // 主键策略
})
.templateEngine(new FreemarkerTemplateEngine())
.execute();
}
}
九、常见问题与解决方案
9.1 驼峰命名映射问题
如果开启了 map-underscore-to-camel-case 后仍映射失败,检查:
- 实体类属性是否使用了 Lombok(确保有 getter/setter)
- 数据库字段名与属性名是否真的匹配(如
userNamevsuser_name)
9.2 SQL 注入问题
始终使用 #{}(预编译参数)而不是 ${}(字符串拼接) 。${} 仅用于无法预编译的场景,如动态表名、列名。务必对用户输入进行严格校验。
9.3 分页失效问题
如果 PageHelper 分页不生效,检查:
PageHelper.startPage()后是否立即执行了查询- 分页参数是否被正确传递
- 是否有其他拦截器干扰
9.4 延迟加载不生效
确保在配置文件中开启 lazyLoadingEnabled=true,且关联查询使用 select 属性指定子查询。
十、总结与展望
本文从 MyBatis 的核心概念、架构原理入手,详细讲解了 Spring Boot 整合 MyBatis 的两种主流方式(XML 与注解),深入剖析了动态 SQL、缓存机制、分页方案、性能优化、多数据源配置等进阶内容,最后介绍了 MyBatis-Plus 这个强大的增强工具。
在实际项目开发中,建议根据场景选择合适的组合:
- 简单项目:Spring Boot + MyBatis(注解方式)+ PageHelper
- 复杂项目:Spring Boot + MyBatis(XML 方式)+ MyBatis-Plus(仅使用增强功能)
- 极速开发:Spring Boot + MyBatis-Plus + 代码生成器
MyBatis 以其灵活性和高性能,在国内 Java 社区拥有极其庞大的用户基础。掌握 MyBatis,不仅是一项必备的 Java 开发技能,更是通向高级工程师道路上的一块重要基石。希望本文能帮助你全面掌握 MyBatis 的核心知识,在实际项目中游刃有余!
📚 参考资料:
- MyBatis 官方文档:https://mybatis.org/mybatis-3/
- MyBatis-Spring-Boot-Starter:https://mybatis.org/spring-boot-starter/
- MyBatis-Plus 官方文档:https://baomidou.com/
- PageHelper 官方文档:https://pagehelper.github.io/