Mybatis-Plus 实战使用与最佳实践
目录
- 引言
- 第一章:MyBATis-Plus概述与环境搭建
- 1.1 Mybatis-Plus简介与核心特性
- 1.1.1 核心特性概览
- 1.1.2 架构设计理念
- 1.2 项目依赖配置与环境搭建
- 1.2.1 Maven依赖配置
- 1.2.2 Gradle依赖配置
- 1.3 数据库连接与基础配置
- 1.3.1 application.yml配置
- 1.3.2 启动类配置
- 1.3.3 Mybatis-Plus配置类
- 第二章:核心注解与基础CRUD操作
- 2.1 实体类注解详解
- 2.1.1 @TableName注解
- 2.1.2 @TableId注解详解
- 2.1.3 @TableField注解应用
- 2.2 BaseMapper接口基础操作
- 2.2.1 BaseMapper接口概述
- 2.2.2 插入操作详解
- 2.2.3 查询操作详解
- 2.2.4 更新操作详解
- 2.2.5 删除操作详解
- 2.3 Service层封装与实战应用
- 2.3.1 IService接口介绍
- 2.3.2 Service层常用方法
- 第三章:条件构造器与复杂查询
- 3.1 QueryWrapper查询条件构造
- 3.1.1 基础条件构造
- 3.1.2 排序与分组
- 3.2 UpdateWrapper更新条件构造
- 3.2.1 基础更新操作
- 3.3 Lambda表达式条件构造器
- 3.3.1 LambdaQueryWrapper使用
- 3.3.2 LambdaUpdateWrapper使用
- 第四章:代码生成器与自动化开发
- 4.1 代码生成器配置与使用
- 4.1.1 代码生成器依赖
- 4.1.2 基础代码生成器配置
- 4.1.3 高级代码生成器配置
- 4.2 自定义模板与代码规范
- 4.2.1 自定义Entity模板
- 4.2.2 自定义Controller模板
- 4.3 批量生成与项目集成
- 4.3.1 批量生成工具类
- 4.3.2 Maven插件集成
- 4.3.3 配置文件管理
- 第五章:高级特性与性能优化
- 5.1 分页插件与性能优化
- 5.1.1 分页插件配置
- 5.1.2 分页查询实战
- 5.1.3 自定义分页查询
- 5.2 逻辑删除与数据安全
- 5.2.1 逻辑删除配置
- 5.2.2 逻辑删除实战应用
- 5.3 自动填充与审计功能
- 5.3.1 自动填充配置
- 5.3.2 审计实体基类
- 5.3.3 乐观锁配置与使用
- 第六章:总结与展望
- 6.1 知识点总结与技术扩展
- 6.1.1 核心知识点回顾
- 6.1.2 技术扩展与深入学习
- 6.2 学习资源与参考资料
- 6.2.1 官方文档与权威资源
- 6.2.2 优质技术博客与教程
- 6.2.3 工具与插件推荐
- 6.3 技术发展趋势与实践建议
- 6.3.1 技术发展趋势分析
- 6.3.2 最佳实践建议
- 6.4 互动与讨论
- 6.4.1 开放性问题探讨
- 6.4.2 实战挑战项目
- 6.4.3 社区交流与学习
- 结语
引言
在现代Java企业级开发中,数据持久层框架的选择直接影响着开发效率和代码质量。Mybatis作为优秀的持久层框架,以其灵活性和可控性赢得了广大开发者的青睐。而Mybatis-Plus作为Mybatis的增强工具,在保持Mybatis原有特性的基础上,提供了更加便捷的CRUD操作、强大的条件构造器、代码生成器等功能,极大地提升了开发效率。
本文将从实战角度出发,深入解析Mybatis-Plus的核心功能和最佳实践,帮助开发者快速掌握这一强大的开发工具。我们将通过丰富的代码示例和实际应用场景,全面展示Mybatis-Plus在企业级项目中的应用价值。

第一章:Mybatis-Plus概述与环境搭建
1.1 Mybatis-Plus简介与核心特性
Mybatis-Plus(简称MP)是一个Mybatis的增强工具,在Mybatis的基础上只做增强不做改变,为简化开发、提高效率而生。它的设计理念是"只做增强不做改变",这意味着引入Mybatis-Plus不会对现有的Mybatis构架产生任何影响。

1.1.1 核心特性概览
Mybatis-Plus具有以下核心特性:
- 无侵入:只做增强不做改变,引入它不会Jkhrc对现有工程产生影响
- 损耗小:启动即会自动注入基本CURD,性能基本无损耗,直接面向对象操作
- 强大的CRUD操作:内置通用Mapper、通用Service,仅仅通过少量配置即可实现单表大部分CRUD操作
- 支持Lambda形式调用:通过Lambda表达式,方便的编写各类查询条件,无需再担心字段写错
- 支持主键自动生成:支持多达4种主键策略,可自由配置,完美解决主键问题
- 支持ActiveRecord模式:支持ActiveRecord形式调用,实体类只需继承Model类即可进行强大的CRUD操作
- 支持自定义全局通用操作:支持全局通用方法注入(Write once, use anywhere)
- 内置代码生成器:采用代码或者Maven插件可快速生成Mapper、Model、Service、Controller层代码
- 内置分页插件:基于Mybatis物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通List查询
- 分页插件支持多种数据库:支持mysql、mariadb、oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer等多种数据库
- 内置性能分析插件:可输出SQL语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
- 内置全局拦截插件:提供全表delete、update操作智能分析阻断,也可自定义拦截规则,预防误操作
1.1.2 架构设计理念

Mybatis-Plus的架构设计遵循以下原则:
// 设计理念示例:只做增强不做改变
public interface UserMapper extends BaseMapper<User> {
// 无需编写任何代码,即可获得强大的CRUD功能
// BaseMapper提供了丰富的CRUD方法
}
// 传统Mybatis方式
public interface TraditionalUserMapper {
int insert(User user);
int deleteById(Long id);
int updateById(User user);
User selectById(Long id);
List<User> selectList();
// 需要编写大量重复的CRUD方法
}
1.2 项目依赖配置与环境搭建
1.2.python1 Maven依赖配置
在SpringBoot项目中集成Mybatis-Plus,首先需要添加相关依赖:
<!-- pom.XML -->
<dependencies>
<!-- SpringBoot Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- SpringBoot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Mybatis-Plus SpringBoot Starter -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3</version>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.16</version>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
1.2.2 Gradle依赖配置
对于使用Gradle的项目:
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'com.baomidou:mybatis-plus-boot-starter:3.5.3'
implementation 'mysql:mysql-connector-java'
implementation 'com.alibaba:druid-spring-boot-starter:1.2.16'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
}
1.3 数据库连接与基础配置
1.3.1 application.yml配置
# application.yml
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mybatis_plus_demo?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8
username: root
password: 123456
# Druid连接池配置
druid:
initial-size: 5
min-idle: 5
max-active: 20
max-wait: 60000
time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 300000
validation-query: SELECT 1
test-while-idle: true
test-on-borrow: false
test-on-return: false
# Mybatis-Plus配置
mybatis-plus:
configuration:
# 开启驼峰命名转换
map-underscore-to-camel-case: true
# 开启SQL日志
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
# 主键策略
id-type: ASSIGN_ID
# 逻辑删除字段
logic-delete-field: deleted
logic-delete-value: 1
logic-not-delete-value: 0
# 扫描mapper文件
mapper-locations: classpath*:/mapper/**/*.xml
# 实体类别名包路径
type-aliases-package: com.example.entity
1.3.2 启动类配置
@SpringBootApplication
@MapperScan("com.example.mapper")
public class MybatisPlusDemoApplication {
public static void main(String[] args) {
SpringApplication.run(MybatisPlusDemoApplication.class, args);
}
}
1.3.3 Mybatis-Plus配置类
@Configuration
@EnableTransactionManagement
public class MybatisPlusConfig {
/**
* 分页插件配置
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 分页插件
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
paginationInnerInterceptor.setDbType(DbType.MYSQL);
paginationInnerInterceptor.setMaxLimit(1000L);
interceptor.addInnerInterceptor(paginationInnerInterceptor);
// 乐观锁插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
/**
* 自动填充配置
*/
@Bean
public MetaObjectHandler metaObjectHandler() {
return new MyMetaObjectHandler();
}
}
/**
* 自动填充处理器
*/
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
this.strictInsertFill(metaObject, "createBy", String.class, getCurrentUser());
this.strictInsertFill(metaObject, "updateBy", String.class, getCurrentUser());
}
@Override
public void updateFill(MetaObject metaObject) {
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
this.strictUpdateFill(metaObject, "updateBy", String.class, getCurrentUser());
}
private String getCurrentUser() {
// 这里可以从SecurityContext或其他地方获取当前用户
return "system";
}
}
第二章:核心注解与基础CRUD操作
2.1 实体类注解详解
2.1.1 @TableName注解
@TableName注解用于指定实体类对应的数据库表名:
@Data
@TableName("sys_user")
public class User {
@TableId(type = IdType.ASSIGN_ID)
private Long id;
private String username;
private String password;
private String email;
private Integer age;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
@TableLogic
private Integer deleted;
}
2.1.2 @TableId注解详解
@TableId注解用于标识主键字段,支持多种主键生成策略:
public class IdTypeExample {
// 自动增长主键
@TableId(type = IdType.AUTO)
private Long autoId;
// 雪花算法ID(默认)
@TableId(type = IdType.ASSIGN_ID)
private Long snowflakeId;
// UUID
@TableId(type = IdType.ASSIGN_UUID)
private String uuidId;
// 手动输入
@TableId(type = IdType.INPUT)
private Long inputId;
// 无主键策略
@TableId(type = IdType.NONE)
private Long noneId;
}
2.1.3 @TableField注解应用
@TableField注解用于配置字段的各种属性:
@Data
@TableName("user_profile")
public class UserProfile {
@TableId
private Long id;
// 指定数据库字段名
@TableField("user_name")
private String username;
// 字段不参与查询
@TableField(select = false)
private String password;
// 字段不存在于数据库
@TableField(exist = false)
private String fullName;
// 自动填充
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
// jsON字段处理
@TableField(typeHandler = JacksonTypeHandler.class)
private List<String> hobbies;
// 条件策略
@TableField(condition = SqlCondition.LIKE)
private String description;
}
2.2 BaseMapper接口基础操作
2.2.1 BaseMapper接口概述
BaseMapper接口提供了丰富的CRUD方法,无需编写SQL即可完成常见的数据库操作:
public interface UserMapper extends BaseMapper<User> {
// 继承BaseMapper后,自动获得以下方法:
// 插入操作:insert
// 删除操作:deleteById, deleteBatchIds, deleteByMap, delete
// 更新操作:updateById, update
// 查询操作:selectById, selectBatchIds, selectByMap, selectOne, selectCount, selectList, selectMaps, selectObjs, selectPage, selectMapsPage
}
2.2.2 插入操作详解
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
/**
* 单条插入
*/
public void insertUser() {
User user = new User();
user.setUsername("张三");
user.setPassword("123456");
user.setEmail("zhangsan@example.com");
user.setAge(25);
// 插入成功后,主键会自动回填到实体对象中
int result = userMapper.insert(user);
System.out.println("插入结果:" + result);
System.out.println("生成的主键:" + user.getId());
}
/**
* 批量插入(需要自定义实现)
*/
public void batchInsert(List<User> userList) {
// Mybatis-Plus没有提供批量插入的BaseMapper方法
// 可以使用Service层的saveBatch方法
// 或者自定义SQL实现真正的批量插入
userList.forEach(user -> userMapper.insert(user));
}
}
2.2.3 查询操作详解
@Service
public class UserQueryService {
@Autowired
private UserMapper userMapper;
/**
* 根据ID查询
*/
public User selectById(Long id) {
return userMapper.selectById(id);
}
/**
* 根据ID批量查询
*/
public List<User> selectByIds(List<Long> ids) {
return userMapper.selectBatchIds(ids);
}
/**
* 根据Map条件查询
*/
public List<User> selectByMap() {
Map<String, Object> columnMap = new HashMap<>();
columnMap.put("age", 25);
columnMap.put("deleted", 0);
return userMapper.selectByMap(columnMap);
}
/**
* 查询所有记录
*/
public List<User> selectAll() {
return userMapper.selectList(null);
}
/**
* 条件查询
*/
public List<User> selectByCondition() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("age", 25)
.like("username", "张")
.isNotNull("email")
.orderByDesc("create_time");
return userMapper.selectList(queryWrapper);
}
/**
* 统计查询
*/
public Long countUsers() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.gt("age", 18);
return userMapper.selectCount(queryWrapper);
}
}
2.2.4 更新操作详解
@Service
public class UserUpdateService {
@Autowired
private UserMapper userMapper;
/**
* 根据ID更新
*/
public void updateById() {
User user = new User();
user.setId(1L);
user.setUsername("李四");
user.setAge(30);
// 只更新非null字段
userMapper.updateById(user);
}
/**
* 条件更新
*/
public void updateByCondition() {
User user = new User();
user.setAge(26);
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("username", "张三")
.set("email", "zhangsan_new@example.com");
userMapper.update(user, updateWrapper);
}
/**
* 直接设置字段值更新
*/
public void updateByWrapper() {
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("age", 25)
.set("age", 26)
.set("update_time", LocalDateTime.now());
userMapper.update(null, updateWrapper);
}
}
2.2.5 删除操作详解
@Service
public class UserDeleteService {
@Autowired
private UserMapper userMapper;
/**
* 根据ID删除
*/
public void deleteById(Long id) {
userMapper.deleteById(id);
}
/**
* 批量删除
*/
public void deleteBatchIds(List<Long> ids) {
userMapper.deleteBatchIds(ids);
}
/**
* 根据Map条件删除
*/
public void deleteByMap() {
Map<String, Object> columnMap = new HashMap<>();
columnMap.put("age", 0);
columnMap.put("username", "test");
userMapper.deleteByMap(columnMap);
}
/**
* 条件删除
*/
public void deleteByCondition() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.lt("age", 18)
.isNull("email");
userMapper.delete(queryWrapper);
}
}
2.3 Service层封装与实战应用
2.3.1 IService接口介绍
Mybatis-Plus提供了IService接口,封装了更多的CRUD操作:
public interface UserService extends IService<User> {
// 继承IService后,自动获得丰富的CRUD方法
// 如:save, saveBatch, saveOrUpdate, remove, update, get, list, page等
}
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
// ServiceImpl已经实现了IService的所有方法
// 可以直接使用,也可以重写自定义实现
}
2.3.2 Service层常用方法
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
/**
* 保存或更新
*/
public void saveOrUpdateUser(User user) {
// 根据主键判断是插入还是更新
this.saveOrUpdate(user);
}
/**
* 批量保存
*/
public void batchSave(List<User> userList) {
// 批量插入,默认批次大小为1000
this.saveBatch(userList);
// 自定义批次大小
this.saveBatch(userList, 500);
}
/**
* 条件查询
*/
public List<User> getActiveUsers() {
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(User::getDeleted, 0)
.gt(User::getAge, 18)
.orderByDesc(User::getCreateTime);
return this.list(queryWrapper);
}
/**
* 分页查询
*/
public IPage<User> getUserPage(int current, int size, String keyword) {
Page<User> page = new Page<>(current, size);
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
if (StringUtils.isNotBlank(keyword)) {
queryWrapper.like(User::getUsername, keyword)
.or()
.like(User::getEmail, keyword);
}
queryWrapper.eq(User::getDeleted, 0)
.orderByDesc(User::getCreateTime);
return this.page(page, queryWrapper);
}
/**
* 统计查询
*/
public long countActiveUsers() {
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(User::getDeleted, 0)
.gt(User::getAge, 18);
return this.count(queryWrapper);
}
}
第三章:条件构造器与复杂查询
3.1 QueryWrapper查询条件构造
3.1.1 基础条件构造
QueryWrapper是Mybatis-Plus提供的查询条件构造器,支持链式调用:
@Service
public class QueryWrapperService {
@Autowired
private UserMapper userMapper;
/**
* 基础条件查询
*/
public List<User> basicQuery() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// 等值查询
queryWrapper.eq("age", 25);
// 不等值查询
queryWrapper.ne("username", "admin");
// 大于、小于查询
queryWrapper.gt("age", 18)
.lt("age", 60);
// 大于等于、小于等于
queryWrapper.ge("create_time", "2023-01-01")
.le("create_time", "2023-12-31");
// 模糊查询
queryWrapper.like("username", "张")
.likeLeft("email", "@qq.com") // %@qq.com
.likeRight("username", "admin"); // admin%
// 空值查询
queryWrapper.isNull("deleted_time")
.isNotNull("email");
// 范围查询
queryWrapper.in("age", Arrays.asList(20, 25, 30))
.notIn("status", Arrays.asList(0, -1));
// 区间查询
queryWrapper.between("age", 20, 30)
.notBetween("create_time", "2023-01-01", "2023-06-01");
return userMapper.selectList(queryWrapper);
}
/**
* 复杂条件组合
*/
public List<User> complexQuery() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// (age > 18 AND age < 60) AND (username LIKE '%admin%' OR email IS NOT NULL)
queryWrapper.gt("age", 18)
.lt("age", 60)
.and(wrapper -> wrapper.like("username", "admin")
.or()
.isNotNull("email"));
// 嵌套查询
queryWrapper.nested(wrapper -> wrapper.eq("status", 1)
.or()
.eq("status", 2));
return userMapper.selectList(queryWrapper);
}
/**
* 动态条件查询
*/
public List<User> dynamicQuery(String username, Integer minAge, Integer maxAge, String email) {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// 条件动态拼接
queryWrapper.like(StringUtils.isNotBlank(username), "username", username)
.ge(minAge != null, "age", minAge)
.le(maxAge != null, "age", maxAge)
.eq(StringUtils.isNotBlank(email), "email", email);
return userMapper.selectList(queryWrapper);
}
}
3.1.2 排序与分组
@Service
public class QueryOrderService {
@Autowired
private UserMapper userMapper;
/**
* 排序查询
*/
public List<User> orderQuery() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// 单字段排序
queryWrapper.orderByAsc("age")
.orderByDesc("create_time");
// 多字段排序
queryWrapper.orderBy(true, true, "age", "username")
.orderBy(true, false, "create_time");
return userMapper.selectList(queryWrapper);
}
/**
* 分组查询
*/
public List<Map<String, Object>> groupQuery() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.select("age", "count(*) as count")
.groupBy("age")
.having("count(*) > 1")
.orderByDesc("count");
return userMapper.selectMaps(queryWrapper);
}
/**
* 字段选择查询
*/
public List<User> selectFields() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// 选择特定字段
queryWrapper.select("id", "username", "email", "age")
.eq("deleted", 0);
return userMapper.selectList(queryWrapper);
}
/**
* 排除字段查询
*/
public List<User> excludeFields() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// 排除敏感字段
queryWrapper.select(User.class, info -> !info.getColumn().equals("password"))
.eq("deleted", 0);
return userMapper.selectList(queryWrapper);
}
}
3.2 UpdateWrapper更新条件构造
3.2.1 基础更新操作
@Service
public class UpdateWrapperService {
@Autowired
private UserMapper userMapper;
/**
* 条件更新
*/
public void updateByCondition() {
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
// 设置更新字段
updateWrapper.set("age", 26)
.set("update_time", LocalDateTime.now())
.eq("username", "张三")
.eq("deleted", 0);
userMapper.update(null, updateWrapper);
}
/**
* 字段自增更新
*/
public void incrementUpdate() {
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
// 年龄自增1
updateWrapper.setSql("age = age + 1")
.eq("id", 1L);
userMapper.update(null, updateWrapper);
}
/**
* 批量条件更新
*/
public void batchUpdate() {
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.set("status", 1)
.set("update_time", LocalDateTime.now())
.in("id", Arrays.asList(1L, 2L, 3L))
.eq("deleted", 0);
userMapper.update(null, updateWrapper);
}
/**
* 复杂条件更新
*/
public void complexUpdate() {
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.set("status", 2)
.set("remark", "批量更新")
.gt("age", 18)
.lt("age", 60)
.and(wrapper -> wrapper.like("username", "test")
.or()
.isNull("email"));
userMapper.update(null, updateWrapper);
}
}
3.3 Lambda表达式条件构造器
3.3.1 LambdaQueryWrapper使用
Lambda表达式条件构造器提供了类型安全的字段引用,避免了字符串硬编码:
@Service
public class LambdaQueryService {
@Autowired
private UserMapper userMapper;
/**
* Lambda查询基础用法
*/
public List<User> lambdaQuery() {
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
// 类型安全的字段引用
queryWrapper.eq(User::getAge, 25)
.like(User::getUsername, "张")
.isNotNull(User::getEmail)
.orderByDesc(User::getCreateTime);
return userMapper.selectList(queryWrapper);
}
/**
* 动态Lambda查询
*/
public List<User> dynamicLambdaQuery(UserQueryDTO queryDTO) {
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.like(StringUtils.isNotBlank(queryDTO.getUsername()),
User::getUsername, queryDTO.getUsername())
.eq(queryDTO.getAge() != null,
User::getAge, queryDTO.getAge())
.ge(queryDTO.getMinAge() != null,
User::getAge, queryDTO.getMinAge())
.le(queryDTO.getMaxAge() != null,
User::getAge, queryDTO.getMaxAge())
.eq(StringUtils.isNotBlank(queryDTO.getEmail()),
User::getEmail, queryDTO.getEmail())
.eq(User::getDeleted, 0)
.orderByDesc(User::getCreateTime);
return userMapper.selectList(queryWrapper);
}
/**
* Lambda分页查询
*/
public IPage<User> lambdaPageQuery(int current, int size, UserQueryDTO queryDTO) {
Page<User> page = new Page<>(current, size);
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
// 构建查询条件
queryWrapper.like(StringUtils.isNotBlank(queryDTO.getUsername()),
User::getUsername, queryDTO.getUsername())
.eq(queryDTO.getStatus() != null,
User::getStatus, queryDTO.getStatus())
.between(queryDTO.getStartTime() != null && queryDTO.getEndTime() != null,
User::getCreateTime, queryDTO.getStartTime(), queryDTO.getEndTime())
.eq(User::getDeleted, 0);
// 排序
if (StringUtils.isNotBlank(queryDTO.getOrderBy())) {
if ("age".equals(queryDTO.getOrderBy())) {
queryWrapper.orderBy(true, queryDTO.isAsc(), User::getAge);
} else if ("createTime".equals(queryDTO.getOrderBy())) {
queryWrapper.orderBy(true, queryDTO.isAsc(), User::getCreateTime);
}
} else {
queryWrapper.orderByDesc(User::getCreateTime);
}
return userMapper.selectPage(page, queryWrapper);
}
}
/**
* 查询DTO
*/
@Data
public class UserQueryDTO {
private String username;
private Integer age;
private Integer minAge;
private Integer maxAge;
private String email;
private Integer status;
private LocalDateTime startTime;
private LocalDateTime endTime;
private String orderBy;
private boolean asc = false;
}
3.3.2 LambdaUpdateWrapper使用
@Service
public class LambdaUpdateService {
@Autowired
private UserMapper userMapper;
/**
* Lambda更新操作
*/
public void lambdaUpdate() {
LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.set(User::getAge, 26)
.set(User::getUpdateTime, LocalDateTime.now())
.eq(User::getUsername, "张三")
.eq(User::getDeleted, 0);
userMapper.update(null, updateWrapper);
}
/**
* 条件Lambda更新
*/
public void conditionalLambdaUpdate(Long userId, UserUpdateDTO updateDTO) {
LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
// 动态设置更新字段
updateWrapper.set(StringUtils.isNotBlank(updateDTO.getUsername()),
User::getUsername, updateDTO.getUsername())
.set(updateDTO.getAge() != null,
User::getAge, updateDTO.getAge())
.set(StringUtils.isNotBlank(updateDTO.getEmail()),
User::getEmail, updateDTO.getEmail())
.set(User::getUpdateTime, LocalDateTime.now())
.eq(User::getId, userId)
.eq(User::getDeleted, 0);
userMapper.update(null, updateWrapper);
}
}
/**
* 更新DTO
*/
@Data
public class UserUpdateDTO {
private String username;
private Integer age;
private String email;
private Integer status;
}
第四章:代码生成器与自动化开发

4.1 代码生成器配置与使用
4.1.1 代码生成器依赖
首先需要添加代码生成器相关依赖:
<!-- 代码生成器 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.3</version>
</dependency>
<!-- 模板引擎 -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.3</version>
</dependency>
4.1.2 基础代码生成器配置
public class CodeGenerator {
public static void main(String[] args) {
// 代码生成器
AutoGenerator mpg = new AutoGenerator();
// 全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath + "/src/main/java");
gc.setAuthor("your-name");
gc.setOpen(false);
gc.setFileOverride(true);
gc.setServiceName("%sService");
gc.setIdType(IdType.ASSIGN_ID);
gc.setDateType(DateType.ONLY_DATE);
gc.setSwagger2(true);
mpg.setGlobalConfig(gc);
// 数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/mybatis_plus_demo?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=GMT%2B8");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("123456");
mpg.setDataSource(dsc);
// 包配置
PackageConfig pc = new PackageConfig();
pc.setModuleName("system");
pc.setParent("com.example");
pc.setEntity("entity");
pc.setMapper("mapper");
pc.setService("service");
pc.setServiceImpl("service.impl");
pc.setController("controller");
mpg.setPackageInfo(pc);
// 自定义配置
InjectionConfig cfg = new InjectionConfig() {
@Override
public void initMap() {
Map<String, Object> map = new HashMap<>();
map.put("date", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
this.setMap(map);
}
};
// 自定义输出配置
List<FileOutConfig> focList = new ArrayList<>();
// 自定义配置会被优先输出
focList.add(new FileOutConfig("/templates/mapper.xml.vm") {
@Override
public String outputFile(TableInfo tableInfo) {
return projectPath + "/src/main/resources/mapper/" + pc.getModuleName()
+ "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
}
});
cfg.setFileOutConfigList(focList);
mpg.setCfg(cfg);
// 配置模板
TemplateConfig templateConfig = new TemplateConfig();
templateConfig.setXml(null);
mpg.setTemplate(templateConfig);
// 策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
strategy.setEntityLombokModel(true);
strategy.setRestControllerStyle(true);
strategy.setInclude("sys_user", "sys_role", "sys_permission");
strategy.setControllerMappingHyphenStyle(true);
strategy.setTablePrefix(pc.getModuleName() + "_");
strategy.setLogicDeleteFieldName("deleted");
strategy.setVersionFieldName("version");
strategy.setEntityTableFieldAnnotationEnable(true);
mpg.setStrategy(strategy);
mpg.setTemplateEngine(new VelocityTemplateEngine());
mpg.execute();
}
}
4.1.3 高级代码生成器配置
@Component
public class AdvancedCodeGenerator {
/**
* 交互式代码生成器
*/
public void interactiveGenerate() {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入表名(多个表名用逗号分隔):");
String tableNames = scanner.nextLine();
System.out.println("请输入模块名:");
String moduleName = scanner.nextLine();
System.out.println("请输入作者名:");
String author = scanner.nextLine();
generateCode(tableNames, moduleName, author);
}
/**
* 代码生成核心方法
*/
private void generateCode(String tableNames, String moduleName, String author) {
// 代码生成器
AutoGenerator mpg = new AutoGenerator();
// 全局配置
GlobalConfig gc = buildGlobalConfig(author);
mpg.setGlobalConfig(gc);
// 数据源配置
DataSourceConfig dsc = buildDataSourceConfig();
mpg.setDataSource(dsc);
// 包配置
PackageConfig pc = buildPackageConfig(moduleName);
mpg.setPackageInfo(pc);
// 自定义配置
InjectionConfig cfg = buildInjectionConfig(pc);
mpg.setCfg(cfg);
// 配置模板
TemplateConfig templateConfig = buildTemplateConfig();
mpg.setTemplate(templateConfig);
// 策略配置
StrategyConfig strategy = buildStrategyConfig(tableNames, pc);
mpg.setStrategy(strategy);
mpg.setTemplateEngine(new VelocityTemplateEngine());
mpg.execute();
}
private GlobalConfig buildGlobalConfig(String author) {
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath + "/src/main/java");
gc.setAuthor(author);
gc.setOpen(false);
gc.setFileOverride(true);
gc.setServiceName("%sService");
gc.setServiceImplName("%sServiceImpl");
gc.setMapperName("%sMapper");
gc.setXmlName("%sMapper");
gc.setControllerName("%sController");
gc.setIdType(IdType.ASSIGN_ID);
gc.setDateType(DateType.TIME_PACK);
gc.setSwagger2(true);
gc.setActiveRecord(false);
gc.setEnableCache(false);
gc.setBaseResultMap(true);
gc.setBaseColumnList(true);
return gc;
}
private DataSourceConfig buildDataSourceConfig() {
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/mybatis_plus_demo?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=GMT%2B8");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("123456");
dsc.setDbType(DbType.MYSQL);
return dsc;
}
private PackageConfig buildPackageConfig(String moduleName) {
PackageConfig pc = new PackageConfig();
pc.setModuleName(moduleName);
pc.setParent("com.example");
pc.setEntity("entity");
pc.setMapper("mapper");
pc.setService("service");
pc.setServiceImpl("service.impl");
pc.setController("controller");
return pc;
}
private InjectionConfig buildInjectionConfig(PackageConfig pc) {
InjectionConfig cfg = new InjectionConfig() {
@Override
public void initMap() {
Map<String, Object> map = new HashMap<>();
map.put("date", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
map.put("author", "Code Generator");
this.setMap(map);
}
};
// 自定义输出配置
List<FileOutConfig> focList = new ArrayList<>();
String projectPath = System.getProperty("user.dir");
// 自定义Mapper XML输出
focList.add(new FileOutConfig("/templates/mapper.xml.vm") {
@Override
public String outputFile(TableInfo tableInfo) {
return projectPath + "/src/main/resources/mapper/" + pc.getModuleName()
+ "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
}
});
// 自定义DTO输出
focList.add(new FileOutConfig("/templates/dto.java.vm") {
@Override
public String outputFile(TableInfo tableInfo) {
return projectPath + "/src/main/java/com/example/" + pc.getModuleName()
+ "/dto/" + tableInfo.getEntityName() + "DTO" + StringPool.DOT_JAVA;
}
});
// 自定义VO输出
focList.add(new FileOutConfig("/templates/vo.java.vm") {
@Override
public String outputFile(TableInfo tableInfo) {
return projectPath + "/src/main/java/com/example/" + pc.getModuleName()
+ "/vo/" + tableInfo.getEntityName() + "VO" + StringPool.DOT_JAVA;
}
});
cfg.setFileOutConfigList(focList);
return cfg;
}
private TemplateConfig buildTemplateConfig() {
TemplateConfig templateConfig = new TemplateConfig();
templateConfig.setXml(null);
return templateConfig;
}
private StrategyConfig buildStrategyConfig(String tableNames, PackageConfig pc) {
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
strategy.setEntityLombokModel(true);
strategy.setRestControllerStyle(true);
strategy.setInclude(tableNames.split(","));
strategy.setControllerMappingHyphenStyle(true);
strategy.setTablePrefix(pc.getModuleName() + "_");
strategy.setLogicDeleteFieldName("deleted");
strategy.setVersionFieldName("version");
strategy.setEntityTableFieldAnnotationEnable(true);
// 字段填充策略
List<TableFill> tableFillList = new ArrayList<>();
tableFillList.add(new TableFill("create_time", FieldFill.INSERT));
tableFillList.add(new TableFill("update_time", FieldFill.INSERT_UPDATE));
tableFillList.add(new TableFill("create_by", FieldFill.INSERT));
tableFillList.add(new TableFill("update_by", FieldFill.INSERT_UPDATE));
strategy.setTableFillList(tableFillList);
return strategy;
}
}
4.2 自定义模板与代码规范
4.2.1 自定义Entity模板
创建自定义Entity模板 /templates/entity.java.vm:
package ${package.Entity};
#foreach($pkg in ${table.importPackages})
import ${pkg};
#end
#if(${swagger2})
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
#end
#if(${entityLombokModel})
import lombok.Data;
import lombok.EqualsAndHashCode;
#end
/**
* ${table.comment!} 实体类
*
* @author ${author}
* @since ${date}
*/
#if(${entityLombokModel})
@Data
#if(${superEntityClass})
@EqualsAndHashCode(callSuper = true)
#else
@EqualsAndHashCode(callSuper = false)
#end
#end
#if(${table.convert})
@TableName("${table.name}")
#end
#if(${swagger2})
@ApiModel(value = "${entity}对象", description = "${table.comment!}")
#end
#if(${superEntityClass})
public class ${entity} extends ${superEntityClass}#if(${activeRecord})<${entity}>#end {
#elseif(${activeRecord})
public class ${entity} extends Model<${entity}> {
#else
public class ${entity} implements Serializable {
#end
private static final long serialVersionUID = 1L;
#foreach($field in ${table.fields})
#if(${field.keyFlag})
#set($keyPropertyName=${field.propertyName})
#end
#if("$!field.comment" != "")
/**
* ${field.comment}
*/
#end
#if(${field.keyFlag})
#if(${field.keyIdentityFlag})
@TableId(value = "${field.annotationColumnName}", type = IdType.AUTO)
#elseif(!$null.isNull(${idType}) && "$!idType" != "")
@TableId(value = "${field.annotationColumnName}", type = IdType.${idType})
#elseif(${field.convert})
@TableId("${field.annotationColumnName}")
#end
#elseif(${field.fill})
#if(${field.convert})
@TableField(value = "${field.annotationColumnName}", fill = FieldFill.${field.fill})
#else
@TableField(fill = FieldFill.${field.fill})
#end
#elseif(${field.convert})
@TableField("${field.annotationColumnName}")
#end
#if("${field.propertyType}" == "Integer")
@ApiModelProperty(value = "${field.comment}")
private ${field.propertyType} ${field.propertyName};
#else
@ApiModelProperty(value = "${field.comment}")
private ${field.propertyType} ${field.propertyName};
#end
#end
#if(!${entityLombokModel})
#foreach($field in ${table.fields})
#if(${field.propertyType.equals("boolean")})
#set($getprefix="is")
#else
#set($getprefix="get")
#end
public ${field.propertyType} ${getprefix}${field.capitalName}() {
return ${field.propertyName};
}
public void set${field.capitalName}(${field.propertyType} ${field.propertyName}) {
this.${field.propertyName} = ${field.propertyName};
}
#end
#end
#if(${activeRecord})
@Override
protected Serializable pkVal() {
#if(${keyPropertyName})
return this.${keyPropertyName};
#else
return null;
#end
}
#end
#if(!${entityLombokModel})
@Override
public String toString() {
return "${entity}{" +
#foreach($field in ${table.fields})
#if($!{foreach.index}==0)
"${field.propertyName}=" + ${field.propertyName} +
#else
", ${field.propertyName}=" + ${field.propertyName} +
#end
#end
"}";
}
#end
}
4.2.2 自定义Controller模板
创建自定义Controller模板 /templates/controller.java.vm:
package ${package.Controller};
import org.springframework.web.bind.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import ${package.Service}.${table.serviceName};
import ${package.Entity}.${entity};
#if(${restControllerStyle})
import org.springframework.web.bind.annotation.RestController;
#else
import org.springframework.stereotype.Controller;
#end
#if(${superControllerClassPackage})
import ${superControllerClassPackage};
#end
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
/**
* ${table.comment!} 前端控制器
*
* @author ${author}
* @since ${date}
*/
#if(${restControllerStyle})
@RestController
#else
@Controller
#end
@RequestMapping("#if(${package.ModuleName})/${package.ModuleName}#end/#if(${controllerMappingHyphenStyle})${controllerMappingHyphen}#else${table.entityPath}#end")
@Api(tags = "${table.comment!}管理")
#if(${kotlin})
class ${table.controllerName}#if(${superControllerClass}) : ${superControllerClass}()#end
#else
#if(${superControllerClass})
public class ${table.controllerName} extends ${superControllerClass} {
#else
public class ${table.controllerName} {
#end
@Autowired
private ${table.serviceName} ${table.entityPath}Service;
/**
* 分页查询
*/
@GetMapping("/page")
@ApiOperation("分页查询${table.comment!}")
public Result<Page<${entity}>> page(@ApiParam("当前页") @RequestParam(defaultValue = "1") Integer current,
@ApiParam("页大小") @RequestParam(defaultValue = "10") Integer size) {
Page<${entity}> page = new Page<>(current, size);
return Result.success(${table.entityPath}Service.page(page));
}
/**
* 根据ID查询
*/
@GetMapping("/{id}")
@ApiOperation("根据ID查询${table.comment!}")
public Result<${entity}> getById(@ApiParam("主键ID") @PathVariable Long id) {
return Result.success(${table.entityPath}Service.getById(id));
}
/**
* 新增
*/
@PostMapping
@ApiOperation("新增${table.comment!}")
public Result<Boolean> save(@RequestBody ${entity} ${table.entityPath}) {
return Result.success(${table.entityPath}Service.save(${table.entityPath}));
}
/**
* 修改
*/
@PutMapping
@ApiOperation("修改${table.comment!}")
public Result<Boolean> updateById(@RequestBody ${entity} ${table.entityPath}) {
return Result.success(${table.entityPath}Service.updateById(${table.entityPath}));
}
/**
* 删除
*/
@DeleteMapping("/{id}")
@ApiOperation("删除${table.comment!}")
public Result<Boolean> removeById(@ApiParam("主键ID") @PathVariable Long id) {
return Result.success(${table.entityPath}Service.removeById(id));
}
}
#end
4.3 批量生成与项目集成
4.3.1 批量生成工具类
@Component
public class BatchCodeGenerator {
/**
* 批量生成指定数据库的所有表
*/
public void generateAllTables(String databaseName) {
List<String> tableNames = getAllTableNames(databaseName);
for (String tableName : tableNames) {
generateSingleTable(tableName, getModuleNameFromTable(tableName));
}
}
/**
* 根据配置文件批量生成
*/
public void generateByConfig(String configPath) {
try {
Properties config = new Properties();
config.load(new FileInputStream(configPath));
String tables = config.getProperty("tables");
String moduleName = config.getProperty("moduleName");
String author = config.getProperty("author");
String packageName = config.getProperty("packageName");
generateCodeWithConfig(tables, moduleName, author, packageName);
} catch (IOException e) {
throw new RuntimeException("读取配置文件失败", e);
}
}
/**
* 获取数据库所有表名
*/
private List<String> getAllTableNames(String databaseName) {
List<String> tableNames = new ArrayList<>();
String sql = "SELECT table_name FROM information_schema.tables WHERE table_schema = ?";
try (Connection connection = getConnection();
PreparedStatement statement = connection.prepareStatement(sql)) {
statement.setString(1, databaseName);
ResultSet resultSet = statement.executeQuery();
while (resultSet.next()) {
tableNames.add(resultSet.getString("table_name"));
}
} catch (SQLException e) {
throw new RuntimeException("获取表名失败", e);
}
return tableNames;
}
/**
* 根据表名推断模块名
*/
private String getModuleNameFromTable(String tableName) {
if (tableName.startsWith("sys_")) {
return "system";
} else if (tableName.startsWith("user_")) {
return "user";
} else if (tableName.startsWith("order_")) {
return "order";
} else {
return "common";
}
}
/**
* 获取数据库连接
*/
private Connection getConnection() throws SQLException {
String url = "jdbc:mysql://localhost:3306/mybatis_plus_demo?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=GMT%2B8";
String username = "root";
String password = "123456";
return DriverManager.getConnection(url, username, password);
}
/**
* 单表生成
*/
private void generateSingleTable(String tableName, String moduleName) {
AutoGenerator mpg = new AutoGenerator();
// 配置生成器
configureGenerator(mpg, tableName, moduleName);
// 执行生成
mpg.execute();
System.out.println("表 " + tableName + " 代码生成完成!");
}
/**
* 配置生成器
*/
private void configureGenerator(AutoGenerator mpg, String tableName, String moduleName) {
// 全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath + "/src/main/java");
gc.setAuthor("Mybatis-Plus Generator");
gc.setOpen(false);
gc.setFileOverride(true);
gc.setServiceName("%sService");
gc.setIdType(IdType.ASSIGN_ID);
gc.setDateType(DateType.TIME_PACK);
gc.setSwagger2(true);
mpg.setGlobalConfig(gc);
// 数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/mybatis_plus_demo?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=GMT%2B8");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("123456");
mpg.setDataSource(dsc);
// 包配置
PackageConfig pc = new PackageConfig();
pc.setModuleName(moduleName);
pc.setParent("com.example");
mpg.setPackageInfo(pc);
// 策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
strategy.setEntityLombokModel(true);
strategy.setRestControllerStyle(true);
strategy.setInclude(tableName);
strategy.setLogicDeleteFieldName("deleted");
strategy.setEntityTableFieldAnnotationEnable(true);
mpg.setStrategy(strategy);
mpg.setTemplatephpEngine(new VelocityTemplateEngine());
}
}
4.3.2 Maven插件集成
在pom.xml中配置Mybatis-Plus代码生成器插件:
<build>
<plugins>
<!-- Mybatis-Plus代码生成器插件 -->
<plugin>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-maven-plugin</artifactId>
<version>1.0.0</version>
<configuration>
<configurationFile>src/main/resources/generator-config.xml</configurationFile>
</configuration>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</www.devze.comdependency>
</dependencies>
</plugin>
</plugins>
</build>
4.3.3 配置文件管理
创建生成器配置文件 generator.properties:
# 数据库配置 jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/mybatis_plus_demo?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=GMT%2B8 jdbc.username=root jdbc.password=123456 # 生成配置 generator.author=Mybatis-Plus Generator generator.packageName=com.example generator.moduleName=system generator.tables=sys_user,sys_role,sys_permission # 输出配置 generator.outputDir=/src/main/java generator.mapperXmlDir=/src/main/resources/mapper # 策略配置 generator.tablePrefix=sys_ generator.logicDeleteField=deleted generator.versionField=version generator.enableLombok=true generator.enableSwagger=true generator.enableRestController=true
第五章:高级特性与性能优化
5.1 分页插件与性能优化
5.1.1 分页插件配置
Mybatis-Plus提供了强大的分页插件,支持多种数据库:
@Configuration
public class MybatisPlusPageConfig {
/**
* 分页插件配置
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 分页插件
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
// 设置数据库类型
paginationInnerInterceptor.setDbType(DbType.MYSQL);
// 设置最大单页限制数量,默认500条,-1不受限制
paginationInnerInterceptor.setMaxLimit(1000L);
// 溢出总页数后是否进行处理
paginationInnerInterceptor.setOverflow(false);
// 生成countSql优化掉join现象
paginationInnerInterceptor.setOptimizeJoin(true);
interceptor.addInnerInterceptor(paginationInnerInterceptor);
return interceptor;
}
}
5.1.2 分页查询实战
@Service
public class UserPageService {
@Autowired
private UserMapper userMapper;
/**
* 基础分页查询
*/
public IPage<User> basicPage(int current, int size) {
Page<User> page = new Page<>(current, size);
return userMapper.selectPage(page, null);
}
/**
* 条件分页查询
*/
public IPage<User> conditionalPage(UserPageQuery query) {
Page<User> page = new Page<>(query.getCurrent(), query.getSize());
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.like(StringUtils.isNotBlank(query.getUsername()),
User::getUsername, query.getUsername())
.eq(query.getStatus() != null,
User::getStatus, query.getStatus())
.between(query.getStartTime() != null && query.getEndTime() != null,
User::getCreateTime, query.getStartTime(), query.getEndTime())
.eq(User::getDeleted, 0)
.orderByDesc(User::getCreateTime);
return userMapper.selectPage(page, queryWrapper);
}
/**
* 自定义分页查询
*/
public IPage<UserVO> customPage(UserPageQuery query) {
Page<UserVO> page = new Page<>(query.getCurrent(), query.getSize());
return userMapper.selectUserPage(page, query);
}
/**
* 不查询总数的分页
*/
public IPage<User> pageWithoutCount(int current, int size) {
Page<User> page = new Page<>(current, size, false);
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(User::getDeleted, 0)
.orderByDesc(User::getCreateTime);
return userMapper.selectPage(page, queryWrapper);
}
}
/**
* 分页查询参数
*/
@Data
public class UserPageQuery {
private Integer current = 1;
private Integer size = 10;
private String username;
private Integer status;
private LocalDateTime startTime;
private LocalDateTime endTime;
private String orderBy;
private Boolean asc = false;
}
/**
* 用户视图对象
*/
@Data
public class UserVO {
private Long id;
private String username;
private String email;
private Integer age;
private String roleName;
private LocalDateTime createTime;
}
5.1.3 自定义分页查询
在Mapper中定义自定义分页查询:
public interface UserMapper extends BaseMapper<User> {
/**
* 自定义分页查询
*/
IPage<UserVO> selectUserPage(Page<UserVO> page, @Param("query") UserPageQuery query);
/**
* 复杂统计分页查询
*/
IPage<UserStatVO> selectUserStatPage(Page<UserStatVO> page, @Param("query") UserStatQuery query);
}
对应的XML映射:
<!-- UserMapper.xml -->
<select id="selectUserPage" resultType="com.example.vo.UserVO">
SELECT
u.id,
u.username,
u.email,
u.age,
r.role_name,
u.create_time
FROM sys_user u
LEFT JOIN sys_user_role ur ON u.id = ur.user_id
LEFT JOIN sys_role r ON ur.role_id = r.id
WHERE u.deleted = 0
<if test="query.username != null and query.username != ''">
AND u.username LIKE CONCAT('%', #{query.username}, '%')
</if>
<if test="query.status != null">
AND u.status = #{query.status}
</if>
<if test="query.startTime != null and query.endTime != null">
AND u.create_time BETWEEN #{query.startTime} AND #{query.endTime}
</if>
ORDER BY u.create_time DESC
</select>
<select id="selectUserStatPage" resultType="com.example.vo.UserStatVO">
SELECT
DATE_FORMAT(u.create_time, '%Y-%m') as month,
COUNT(*) as userCount,
COUNT(CASE WHEN u.status = 1 THEN 1 END) as activeCount,
AVG(u.age) as avgAge
FROM sys_user u
WHERE u.deleted = 0
<if test="query.startTime != null and query.endTime != null">
AND u.create_time BETWEEN #{query.startTime} AND #{query.endTime}
</if>
GROUP BY DATE_FORMAT(u.create_time, '%Y-%m')
ORDER BY month DESC
</select>
5.2 逻辑删除与数据安全
5.2.1 逻辑删除配置
@Configuration
public class LogicDeleteConfig {
/**
* 逻辑删除配置
*/
@Bean
public ISqlInjector sqlInjector() {
return new LogicSqlInjector();
}
}
在实体类中配置逻辑删除字段:
@Data
@TableName("sys_user")
public class User {
@TableId(type = IdType.ASSIGN_ID)
private Long id;
private String username;
private String password;
private String email;
private Integer age;
/**
* 逻辑删除字段
* 0-未删除,1-已删除
*/
@TableLogic
@TableField(value = "deleted", fill = FieldFill.INSERT)
private Integer deleted;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
}
5.2.2 逻辑删除实战应用
@Service
public class LogicDeleteService {
@Autowired
private UserMapper userMapper;
/**
* 逻辑删除单个用户
*/
public boolean logicDeleteUser(Long userId) {
// 调用deleteById会自动进行逻辑删除
return userMapper.deleteById(userId) > 0;
}
/**
* 批量逻辑删除
*/
public boolean batchLogicDelete(List<Long> userIds) {
return userMapper.deleteBatchIds(userIds) > 0;
}
/**
* 条件逻辑删除
*/
public boolean logicDeleteByCondition(Integer status) {
LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(User::getStatus, status)
.set(User::getDeleted, 1)
.set(User::getUpdateTime, LocalDateTime.now());
return userMapper.update(null, updateWrapper) > 0;
}
/**
* 查询包含已删除的数据
*/
public List<User> selectWithDeleted() {
// 使用原生SQL查询,绕过逻辑删除
return userMapper.selectList(new QueryWrapper<User>().last("/* 包含已删除数据 */"));
}
/**
* 恢复逻辑删除的数据
*/
public boolean restoreUser(Long userId) {
LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(User::getId, userId)
.set(User::getDeleted, 0)
.set(User::getUpdateTime, LocalDateTime.now());
return userMapper.update(null, updateWrapper) > 0;
}
/**
* 物理删除(真正删除数据)
*/
public boolean physicalDelete(Long userId) {
// 需要自定义SQL实现物理删除
return userMapper.physicalDeleteById(userId) > 0;
}
}
在Mapper中添加物理删除方法:
public interface UserMapper extends BaseMapper<User> {
/**
* 物理删除用户
*/
@Delete("DELETE FROM sys_user WHERE id = #{userId}")
int physicalDeleteById(@Param("userId") Long userId);
/**
* 查询包含已删除的用户
*/
@Select("SELECT * FROM sys_user WHERE id = #{userId}")
User selectByIdwithDeleted(@Param("userId") Long userId);
}
5.3 自动填充与审计功能
5.3.1 自动填充配置
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
private static final Logger log = LoggerFactory.getLogger(MyMetaObjectHandler.class);
/**
* 插入时自动填充
*/
@Override
public void insertFill(MetaObject metaObject) {
log.info("开始插入填充...");
// 填充创建时间
this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
// 填充更新时间
this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
// 填充创建人
this.strictInsertFill(metaObject, "createBy", String.class, getCurrentUserId());
// 填充更新人
this.strictInsertFill(metaObject, "updateBy", String.class, getCurrentUserId());
// 填充逻辑删除字段
this.strictInsertFill(metaObject, "deleted", Integer.class, 0);
// 填充版本号
this.strictInsertFill(metaObject, "version", Integer.class, 1);
}
/**
* 更新时自动填充
*/
@Override
public void updateFill(MetaObject metaObject) {
log.info("开始更新填充...");
// 填充更新时间
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
// 填充更新人
this.strictUpdateFill(metaObject, "updateBy", String.class, getCurrentUserId());
}
/**
* 获取当前用户ID
*/
private String getCurrentUserId() {
// 从Spring Security上下文获取当前用户
try {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null && authentication.isAuthenticated()) {
Object principal = authentication.getPrincipal();
if (principal instanceof UserDetails) {
return ((UserDetails) principal).getUsername();
} else {
return principal.toString();
}
}
} catch (Exception e) {
log.warn("获取当前用户失败", e);
}
// 从请求头获取用户信息
try {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
String userId = request.getHeader("X-User-Id");
if (StringUtils.isNotBlank(userId)) {
return userId;
}
} catch (Exception e) {
log.warn("从请求头获取用户ID失败", e);
}
return "system";
}
}
5.3.2 审计实体基类
@Data
@MappedSuperclass
public abstract class BaseAuditEntity {
/**
* 创建时间
*/
@TableField(value = "create_time", fill = FieldFill.INSERT)
private LocalDateTime createTime;
/**
* 更新时间
*/
@TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
/**
* 创建人
*/
@TableField(value = "create_by", fill = FieldFill.INSERT)
private String createBy;
/**
* 更新人
*/
@TableField(value = "update_by", fill = FieldFill.INSERT_UPDATE)
private String updateBy;
/**
* 逻辑删除
*/
@TableLogic
@TableField(value = "deleted", fill = FieldFill.INSERT)
private Integer deleted;
/**
* 版本号(乐观锁)
*/
@Version
@TableField(value = "version", fill = FieldFill.INSERT)
private Integer version;
}
继承审计基类的实体:
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("sys_user")
public class User extends BaseAuditEntity {
@TableId(type = IdType.ASSIGN_ID)
private Long id;
private String username;
private String password;
private String email;
private Integer age;
private Integer status;
}
5.3.3 乐观锁配置与使用
@Configuration
public class OptimisticLockerConfig {
/**
* 乐观锁插件
*/
@Bean
public MybatisPlusInterceptor optimisticLockerInterceptor() {javascript
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
}
乐观锁使用示例:
@Service
public class OptimisticLockService {
@Autowired
private UserMapper userMapper;
/**
* 乐观锁更新示例
*/
public boolean updateWithOptimisticLock(Long userId, String newEmail) {
// 先查询获取当前版本号
User user = userMapper.selectById(userId);
if (user == null) {
return false;
}
// 更新数据,版本号会自动+1
user.setEmail(newEmail);
int result = userMapper.updateById(user);
// result为0表示更新失败(版本号冲突)
return result > 0;
}
/**
* 重试机制的乐观锁更新
*/
public boolean updateWithRetry(Long userId, String newEmail, int maxRetries) {
for (int i = 0; i < maxRetries; i++) {
User user = userMapper.selectById(userId);
if (user == null) {
return false;
}
user.setEmail(newEmail);
int result = userMapper.updateById(user);
if (result > 0) {
return true;
}
// 短暂等待后重试
try {
Thread.sleep(10);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
}
}
return false;
}
}
第六章:总结与展望
6.1 知识点总结与技术扩展
通过本文的深入学习,我们全面掌握了Mybatis-Plus的核心功能和实战应用。让我们来总结一下关键知识点:
6.1.1 核心知识点回顾
基础配置与环境搭建
- Mybatis-Plus的"只做增强不做改变"设计理念
- SpringBoot集成配置和依赖管理
- 数据源配置和连接池优化
- 全局配置和插件配置
实体注解与CRUD操作
@TableName、@TableId、@TableField等核心注解- BaseMapper接口提供的丰富CRUD方法
- IService接口的高级封装和批量操作
- 自动填充和审计功能的实现
条件构造器与复杂查询
- QueryWrapper和UpdateWrapper的灵活使用
- Lambda表达式条件构造器的类型安全特性
- 动态条件构建和复杂查询组合
- 分页查询和自定义SQL的结合
代码生成器与自动化开发
- 代码生成器的配置和自定义模板
- 批量生成和项目集成策略
- Maven插件和配置文件管理
- 企业级代码规范和最佳实践
高级特性与性能优化
- 分页插件的配置和性能优化
- 逻辑删除的实现和数据安全保障
- 乐观锁机制和并发控制
- 自动填充和审计日志功能
6.1.2 技术扩展与深入学习
微服务架构中的应用
// 分布式事务中的Mybatis-Plus应用
@Service
@Transactional
public class DistributedUserService {
@Autowired
private UserMapper userMapper;
@Autowired
private OrderService orderService;
/**
* 分布式事务场景下的用户创建
*/
@GlobalTransactional
public void createUserWithOrder(User user, Order order) {
// 创建用户
userMapper.insert(user);
// 调用订单服务
orderService.createOrder(order);
}
}
缓存集成与性能优化
// Redis缓存集成
@Service
public class CachedUserService extends ServiceImpl<UserMapper, User> {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Cacheable(value = "user", key = "#id")
public User getById(Long id) {
return super.getById(id);
}
@CacheEvict(value = "user", key = "#user.id")
public boolean updateById(User user) {
return super.updateById(user);
}
}
多数据源配置
@Configuration
public class MultiDataSourceConfig {
@Primary
@Bean("masterDataSource")
public DataSource masterDataSource() {
return DataSourceBuilder.create().build();
}
@Bean("slaveDataSource")
public DataSource slaveDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
public DynamicDataSource dynamicDataSource() {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
Map<Object, Object> dataSourceMap = new HashMap<>();
dataSourceMap.put("master", masterDataSource());
dataSourceMap.put("slave", slaveDataSource());
dynamicDataSource.setTargetDataSources(dataSourceMap);
dynamicDataSource.setDefaultTargetDataSource(masterDataSource());
return dynamicDataSource;
}
}
6.2 学习资源与参考资料
6.2.1 官方文档与权威资源
官方文档
- Mybatis-Plus官方文档 - 最权威的学习资源
- Mybatis官方文档 - 理解底层原理
- Spring Boot官方文档 - 集成配置参考
权威书籍推荐
- 《MyBatis从入门到精通》- 刘增辉著,系统学习MyBatis基础
- 《Spring Boot实战》- Craig Walls著,深入理解SpringBoot
- 《Java并发编程实战》- Brian Goetz著,理解并发和锁机制
- 《高性能MySQL》- Baron Schwartz著,数据库性能优化
6.2.2 优质技术博客与教程
技术社区推荐
- 掘金技术社区 - 丰富的Mybatis-Plus实战文章
- CSDN博客平台 - 大量的技术分享和问题解答
- 博客园 - 深度技术文章和经验分享
- 思否SegmentFault - 问答社区和技术讨论
开源项目学习
- RuoYi-vue - 基于SpringBoot+Mybatis-Plus的管理系统
- Pig - 微服务架构的快速开发平台
- Jeecg-Boot - 低代码开发平台
- SpringBlade - 微服务架构的开发平台
6.2.3 工具与插件推荐
开发工具
- MybatisX插件 - IDEA中的Mybatis增强插件,提供XML和Mapper跳转
- Free Mybatis Plugin - 免费的Mybatis插件,支持代码生成
- MyBatis Log Plugin - SQL日志格式化插件
- Database Navigator - 数据库管理和SQL执行工具
性能监控工具
- Druid监控 - 阿里巴巴开源的数据库连接池监控
- P6Spy - SQL语句拦截和性能分析
- SkyWalking - 分布式系统性能监控
- Arthas - Java应用诊断工具
6.3 技术发展趋势与实践建议
6.3.1 技术发展趋势分析
云原生数据库支持
随着云计算的发展,Mybatis-Plus正在加强对云原生数据库的支持,包括:- 多云数据库适配
- Serverless数据库集成
- 云数据库的自动扩缩容支持
响应式编程支持
// 未来可能的响应式API设计
@Service
public class ReactiveUserService {
@Autowired
private ReactiveUserMapper userMapper;
public Mono<User> findById(Long id) {
return userMapper.selectById(id);
}
public Flux<User> findAll() {
return userMapper.selectAll();
}
}
AI辅助开发
- 智能SQL优化建议
- 自动化性能调优
- 基于机器学习的查询优化
6.3.2 最佳实践建议
性能优化建议
- 合理使用分页查询,避免大数据量查询
- 启用SQL日志,监控慢查询并优化
- 使用连接池监控,及时发现连接泄露
- 合理设置缓存策略,提高查询性能
代码规范建议
- 统一实体类设计,继承审计基类
- 规范命名约定,保持代码可读性
- 合理使用条件构造器,避免复杂的嵌套查询
- 编写单元测试,保证代码质量
安全性建议
- 启用逻辑删除,保护重要数据
- 使用乐观锁,处理并发更新
- 参数校验,防止SQL注入
- 敏感字段加密,保护用户隐私
6.4 互动与讨论
6.4.1 开放性问题探讨
让我们一起思考以下几个深度技术问题:
问题1:性能优化策略
在高并发场景下,如何平衡Mybatis-Plus的便利性和性能要求?你会选择哪些优化策略?问题2:微服务架构适配
在微服务架构中,如何设计Mybatis-Plus的数据访问层?如何处理分布式事务和数据一致性?问题3:代码生成器定制
如何根据企业的代码规范,定制适合团队的代码生成器模板?有哪些最佳实践?问题4:多数据源管理
在复杂的业务场景中,如何优雅地管理多数据源?如何实现读写分离和数据库切换?问题5:未来发展方向
你认为Mybatis-Plus在云原生和响应式编程方面应该如何发展?有哪些期待的新特性?6.4.2 实战挑战项目
挑战项目1:企业级用户管理系统
使用Mybatis-Plus构建一个完整的用户管理系统,包括:- 用户注册、登录、权限管理
- 分页查询、条件搜索、批量操作
- 审计日志、逻辑删除、乐观锁
- 性能监控、SQL优化、缓存集成
挑战项目2:微服务数据访问层
设计一个微服务架构的数据访问层,实现:- 多数据源动态切换
- 分布式事务管理
- 读写分离和负载均衡
- 数据同步和一致性保证
6.4.3 社区交流与学习
技术交流方式
- github讨论区 - 参与开源项目讨论,提交Issue和PR
- 技术QQ群/微信群 - 加入Mybatis-Plus技术交流群
- 技术会议和Meetup - 参加线下技术分享活动
- 博客和视频分享 - 分享自己的学习心得和实战经验
持续学习建议
- 关注官方动态 - 及时了解新版本特性和更新
- 实践项目应用 - 在实际项目中应用所学知识
- 参与开源贡献 - 为开源项目贡献代码和文档
- 分享学习心得 - 通过博客、视频等方式分享经验
结语
Mybatis-Plus作为优秀的持久层增强工具,极大地提升了Java开发者的工作效率。通过本文的深入学习,相信你已经掌握了从基础配置到高级特性的全面知识。
技术的学习是一个持续的过程,希望这篇文章能够成为你Mybatis-Plus学习路上的重要参考。在实际项目中,要根据具体业务需求选择合适的技术方案,在便利性和性能之间找到最佳平衡点。
到此这篇关于Mybatis-Plus 实战使用与最佳实践的文章就介绍到这了,更多相关Mybatis-Plus 使用内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!
加载中,请稍侯......
精彩评论