MyBatis Plus 使用详解
一、添加依赖与配置 MySQL
1. 添加依赖(Maven)
<!-- MyBatis Plus 核心依赖 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
<!-- MySQL 驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>
2. Mybatis Plus的详细yml配置
# application.yml
# 数据源配置
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mybatis_plus_demo?useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username: root
password: 123456
# MyBatis Plus 配置(继承 MyBatis 原生配置)
mybatis-plus:
# 全局配置(核心配置)
global-config:
db-config:
id-type: auto # 主键策略:AUTO(数据库自增)、INPUT(手动输入)、ASSIGN_ID(雪花算法)
logic-delete-field: deleted # 逻辑删除字段名(实体类字段名)
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
table-underline: true # 表名是否使用下划线命名(默认 true)
column-underline: true # 字段名是否使用下划线命名(默认 true)
configuration:
map-underscore-to-camel-case: true # 自动驼峰命名规则(默认 true)
cache-enabled: true # 开启二级缓存(默认 true)
lazy-loading-enabled: true # 延迟加载(默认 false)
aggressive-lazy-loading: false # 侵略性延迟加载(默认 false)
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 日志实现(控制台打印 SQL)
# MyBatis 原生配置(继承关系)
mapper-locations: classpath*:/mapper/**/*.xml # XML 映射文件路径
type-aliases-package: com.example.entity # 实体类别名包路径
type-handlers-package: com.example.handler # 类型处理器包路径
# 插件配置(需配合 Java Config 使用)
# 注意:以下插件需在配置类中通过 @Bean 注入
# 分页插件(必须配置)
# 性能分析插件(开发环境使用)
# 乐观锁插件
二、CRUD 实现与代码示例
1. 实体类定义
@Data
@TableName("user") // 表名映射(默认类名驼峰转下划线)
public class User {
@TableId(type = IdType.AUTO) // 主键自增
private Long id;
private String name;
private Integer age;
private String email;
}
2. Mapper 接口继承 BaseMapper
无需编写 XML,继承 BaseMapper 即可获得 CRUD 方法
public interface UserMapper extends BaseMapper<User> {
// 无需编写 XML,继承 BaseMapper 即可获得 CRUD 方法
}
3. CRUD 操作代码示例
@Autowired
private UserMapper userMapper;
// 插入
User user = new User();
user.setName("张三");
user.setAge(25);
user.setEmail("zhangsan@example.com");
userMapper.insert(user); // 返回影响行数
// 查询(根据ID)
User user = userMapper.selectById(1L);
// 查询所有
List<User> users = userMapper.selectList(null);
// 条件查询
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("age", 25);
List<User> userList = userMapper.selectList(wrapper);
// 更新
user.setAge(30);
userMapper.updateById(user); // 根据ID更新
// 删除
userMapper.deleteById(1L);
三、条件构造器(Wrapper)详解
1. 核心类说明
QueryWrapper
:普通条件构造器(字段名硬编码)LambdaQueryWrapper
:Lambda 表达式条件构造器(避免字段名硬编码)(推荐)
2. 常用条件方法
方法名 | 作用 | 示例 |
---|---|---|
eq | 等于 | eq("name", "张三") |
ne | 不等于 | ne("status", 0) |
gt | 大于 | gt("age", 18) |
between | 范围查询 | between("age", 20, 30) |
like | 模糊查询 | like("name", "张%") |
in | 包含 | in("id", Arrays.asList(1, 2, 3)) |
orderByAsc | 升序排序 | orderByAsc("age") |
select | 指定查询字段 | select("id", "name") |
3. 代码示例
// 1. 查询年龄大于20且邮箱包含"example"的用户
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.gt("age", 20)
.like("email", "example")
.orderByDesc("id");
List<User> users = userMapper.selectList(wrapper);
// 2. 使用 Lambda 表达式(避免字段名硬编码)
LambdaQueryWrapper<User> lambdaWrapper = new LambdaQueryWrapper<>();
lambdaWrapper.gt(User::getAge, 20)
.like(User::getEmail, "example")
.orderByDesc(User::getId);
List<User> lambdaUsers = userMapper.selectList(lambdaWrapper);
// 3. 复杂组合条件(AND/OR)
wrapper.and(w -> w.eq("name", "张三").or().eq("name", "李四"))
.between("age", 20, 30);
四、分页插件使用详解
1. 配置分页插件
@Configuration
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加分页插件(数据库类型需指定)
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
2. 分页查询代码示例
// 1. 创建分页对象(当前页, 每页数量)
Page<User> page = new Page<>(1, 10);
// 2. 执行分页查询(传入分页对象和查询条件)
Page<User> result = userMapper.selectPage(page, null);
// 3. 获取结果
System.out.println("总记录数: " + result.getTotal());
System.out.println("当前页数据: " + result.getRecords());
// 4. 自定义分页查询(如关联多表)
userMapper.selectUserPage(page, wrapper); // 需在Mapper中定义SQL
五、代码生成器详解
1. 添加依赖
<!-- 代码生成器 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.3.1</version>
</dependency>
<!-- 模板引擎(Freemarker) -->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.31</version>
</dependency>
2. 生成器配置代码
public class CodeGenerator {
public static void main(String[] args) {
AutoGenerator generator = new AutoGenerator();
// 1. 全局配置
GlobalConfig globalConfig = new GlobalConfig();
globalConfig.setOutputDir(System.getProperty("user.dir") + "/src/main/java");
globalConfig.setAuthor("YourName");
globalConfig.setOpen(false); // 生成后不打开文件夹
globalConfig.setFileOverride(true); // 覆盖旧文件
generator.setGlobalConfig(globalConfig);
// 2. 数据源配置
DataSourceConfig dataSource = new DataSourceConfig();
dataSource.setUrl("jdbc:mysql://localhost:3306/mybatis_plus_demo?useSSL=false");
dataSource.setDriverName("com.mysql.cj.jdbc.Driver");
dataSource.setUsername("root");
dataSource.setPassword("123456");
generator.setDataSource(dataSource);
// 3. 包路径配置
PackageConfig packageConfig = new PackageConfig();
packageConfig.setParent("com.example"); // 父包名
packageConfig.setEntity("entity"); // 实体类包名
packageConfig.setMapper("mapper"); // Mapper包名
generator.setPackageInfo(packageConfig);
// 4. 策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel); // 表名转驼峰
strategy.setColumnNaming(NamingStrategy.underline_to_camel); // 列名转驼峰
strategy.setEntityLombokModel(true); // 使用Lombok
strategy.setInclude("user", "order"); // 指定生成哪些表
strategy.setRestControllerStyle(true); // 生成@RestController
generator.setStrategy(strategy);
// 5. 执行生成
generator.setTemplateEngine(new FreemarkerTemplateEngine());
generator.execute();
}
}
3. 生成结果
运行 main
方法后,会自动生成以下文件:
User.java
(实体类)UserMapper.java
(Mapper接口)UserMapper.xml
(XML文件,默认无内容,需自定义SQL)UserService.java
(Service接口)UserServiceImpl.java
(Service实现类)
六、继承 IService
的核心步骤
一. 继承父类接口
可以简化在service层的代码书写,前提是需要让Mapper层继承BaseMapper< >,让Service继承Iservice<>
1. 创建实体类(Entity)
定义与数据库表对应的实体类,使用 MyBatis Plus 注解:
@Data
@TableName("user") // 指定表名(若表名与类名一致可省略)
public class User {
@TableId(type = IdType.AUTO) // 主键自增
private Long id;
private String name;
private Integer age;
private String email;
@TableField(fill = FieldFill.INSERT) // 自动填充创建时间
private LocalDateTime createTime;
@TableLogic // 逻辑删除标记
private Integer deleted;
}
2. 创建 Mapper 接口
Mapper 接口继承 BaseMapper<T>
,无需编写 XML:
public interface UserMapper extends BaseMapper<User> {
// 可在此定义自定义 SQL 方法(需配合 XML 或注解)
}
3. 定义 Service 接口
Service 接口继承 IService<T>
,声明通用方法 + 自定义业务方法:
public interface UserService extends IService<User> {
// 自定义方法示例:查询活跃用户列表
List<User> findActiveUsers();
// 自定义方法示例:分页查询成年人
Page<User> pageAdults(Page<User> page);
}
4. 实现 Service 接口
实现类继承 ServiceImpl<M, T>
,并实现自定义方法:
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
// 实现自定义方法:查询活跃用户(status=1)
@Override
public List<User> findActiveUsers() {
return this.lambdaQuery()
.eq(User::getStatus, 1)
.list();
}
// 实现自定义方法:分页查询成年人(age >= 18)
@Override
public Page<User> pageAdults(Page<User> page) {
return this.page(page,
new LambdaQueryWrapper<User>().ge(User::getAge, 18)
);
}
}
二、IService
核心方法详解
1. 基础 CRUD 方法
方法名 | 作用 | 示例 |
---|---|---|
save(T entity) | 插入单条记录 | userService.save(user) |
saveBatch(List<T> list) | 批量插入 | userService.saveBatch(userList) |
removeById(Serializable id) | 根据 ID 删除 | userService.removeById(1L) |
updateById(T entity) | 根据 ID 更新 | userService.updateById(user) |
getById(Serializable id) | 根据 ID 查询 | User user = userService.getById(1L) |
list() | 查询所有记录 | List<User> users = userService.list() |
list(Wrapper<T> wrapper) | 条件查询列表 | userService.list(wrapper) |
2. 链式查询(Lambda 风格)
// 查询年龄大于18且邮箱包含"example"的用户
List<User> users = userService.lambdaQuery()
.gt(User::getAge, 18)
.like(User::getEmail, "example")
.list();
// 更新所有名字以"张"开头的用户的年龄
boolean updated = userService.lambdaUpdate()
.set(User::getAge, 25)
.likeRight(User::getName, "张")
.update();
3. 分页查询
// 分页查询(需先配置分页插件)
Page<User> page = new Page<>(1, 10); // 当前页, 每页数量
Page<User> result = userService.page(page,
new LambdaQueryWrapper<User>().orderByDesc(User::getCreateTime)
);
// 获取分页结果
System.out.println("总记录数: " + result.getTotal());
System.out.println("当前页数据: " + result.getRecords());
4. 批量操作
// 批量插入(每批 1000 条)
userService.saveBatch(userList, 1000);
// 批量更新(根据 ID)
userService.updateBatchById(userList);
// 批量删除(根据 ID 列表)
userService.removeByIds(Arrays.asList(1L, 2L, 3L));
三、自定义业务方法扩展
1. 结合自定义 Mapper 方法
如果操作需要复杂 SQL,可在 Mapper 中定义,并在 Service 中调用:
Mapper 接口:
public interface UserMapper extends BaseMapper<User> {
// 自定义查询(如联表查询)
List<User> selectUserWithRole(Long userId);
}
Service 实现类:
@Override
public List<User> findActiveUsers() {
// 调用自定义 Mapper 方法
return baseMapper.selectUserWithRole(1L);
}
2. 覆盖默认方法
修改默认 CRUD 逻辑(如添加数据校验):
@Override
public boolean save(User user) {
// 自定义校验逻辑
if (user.getName() == null) {
throw new RuntimeException("用户名不能为空");
}
return super.save(user);
}
四、事务管理与注解
1. 开启事务
默认情况下,MyBatis Plus 不自动开启事务,需手动添加 @Transactional
:
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Transactional // 添加事务注解
@Override
public void transferBalance(Long fromUserId, Long toUserId, BigDecimal amount) {
userMapper.deductBalance(fromUserId, amount);
userMapper.addBalance(toUserId, amount);
}
}
2. 事务传播行为
通过 @Transactional(propagation = Propagation.REQUIRED)
指定事务传播级别。
五、最佳实践
1. 严格分层架构
- Controller 层:处理 HTTP 请求,调用 Service 方法。
- Service 层:实现业务逻辑,调用 Mapper 或组合多个 Service。
- Mapper 层:仅负责数据库操作。
2. 避免过度封装
- 简单操作:直接使用
IService
提供的方法。 - 复杂操作:优先在 Service 层组合多个方法,或使用自定义 Mapper。
3. 性能优化
- 批量操作:使用
saveBatch
/updateBatchById
代替循环单条操作。 - 分页查询:避免不必要的大数据量分页(如
Page
的searchCount
配置)。
六、完整代码示例
Controller 层
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/{id}")
public User getById(@PathVariable Long id) {
return userService.getById(id);
}
@PostMapping("/save")
public boolean save(@RequestBody User user) {
return userService.save(user);
}
}
Service 层完整实现
public interface UserService extends IService<User> {
List<User> findAdults();
Page<User> pageAdults(Page<User> page);
List<User> findVIPUsers();
}
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Override
public List<User> findAdults() {
return this.lambdaQuery().ge(User::getAge, 18).list();
}
@Override
public Page<User> pageAdults(Page<User> page) {
return this.page(page, new LambdaQueryWrapper<User>().ge(User::getAge, 18));
}
@Override
public List<User> findVIPUsers() {
return baseMapper.selectVIPUsers(); // 调用 Mapper 中的自定义方法
}
}
七、常见问题解决
1. Service 方法不生效
- 检查是否添加了
@Service
注解。 - 确保 Service 实现类继承了
ServiceImpl<M, T>
。
2. 事务不回滚
- 确认方法是否为
public
。 - 检查是否在同一个类中调用事务方法(避免自调用)。
3. 字段映射失败
- 检查
@TableField
注解的value
属性是否与数据库字段名一致。 - 确认
application.yml
中map-underscore-to-camel-case
配置是否开启。
通过继承 IService
,开发者可以快速构建出功能强大且易于维护的 Service 层,既能享受 MyBatis Plus 的便捷性,又能灵活扩展业务逻辑。
通过以上步骤,可快速掌握 MyBatis Plus 的核心功能。实际开发中可结合官方文档进一步优化配置。MybatisPlus官网:官网地址