【JAVA 进阶】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具有以下核心特性:
- 无侵入:只做增强不做改变,引入它不会对现有工程产生影响
- 损耗小:启动即会自动注入基本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.1 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.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/mybatis_plus_demo?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8username: rootpassword: 123456# Druid连接池配置druid:initial-size: 5min-idle: 5max-active: 20max-wait: 60000time-between-eviction-runs-millis: 60000min-evictable-idle-time-millis: 300000validation-query: SELECT 1test-while-idle: truetest-on-borrow: falsetest-on-return: false# Mybatis-Plus配置
mybatis-plus:configuration:# 开启驼峰命名转换map-underscore-to-camel-case: true# 开启SQL日志log-impl: org.apache.ibatis.logging.stdout.StdOutImplglobal-config:db-config:# 主键策略id-type: ASSIGN_ID# 逻辑删除字段logic-delete-field: deletedlogic-delete-value: 1logic-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 {/*** 分页插件配置*/@Beanpublic 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;}/*** 自动填充配置*/@Beanpublic MetaObjectHandler metaObjectHandler() {return new MyMetaObjectHandler();}
}/*** 自动填充处理器*/
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {@Overridepublic 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());}@Overridepublic 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;@TableLogicprivate 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 {@TableIdprivate 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 {@Autowiredprivate 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 {@Autowiredprivate 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 {@Autowiredprivate 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 {@Autowiredprivate 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) {// 批量插入,默认批次大小为1000this.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 {@Autowiredprivate 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 {@Autowiredprivate 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 {@Autowiredprivate 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<>();// 年龄自增1updateWrapper.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 {@Autowiredprivate 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 {@Autowiredprivate 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() {@Overridepublic 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") {@Overridepublic 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() {@Overridepublic 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") {@Overridepublic 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") {@Overridepublic 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") {@Overridepublic 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 {
#endprivate 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")
#endpublic ${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})@Overrideprotected Serializable pkVal() {
#if(${keyPropertyName})return this.${keyPropertyName};
#elsereturn null;
#end}#end
#if(!${entityLombokModel})@Overridepublic 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@Autowiredprivate ${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.setTemplateEngine(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></dependency></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 {/*** 分页插件配置*/@Beanpublic 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 {@Autowiredprivate 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_timeFROM sys_user uLEFT JOIN sys_user_role ur ON u.id = ur.user_idLEFT JOIN sys_role r ON ur.role_id = r.idWHERE 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 avgAgeFROM sys_user uWHERE 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 {/*** 逻辑删除配置*/@Beanpublic 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 {@Autowiredprivate 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);/*** 插入时自动填充*/@Overridepublic 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);}/*** 更新时自动填充*/@Overridepublic 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 {/*** 乐观锁插件*/@Beanpublic MybatisPlusInterceptor optimisticLockerInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());return interceptor;}
}
乐观锁使用示例:
@Service
public class OptimisticLockService {@Autowiredprivate UserMapper userMapper;/*** 乐观锁更新示例*/public boolean updateWithOptimisticLock(Long userId, String newEmail) {// 先查询获取当前版本号User user = userMapper.selectById(userId);if (user == null) {return false;}// 更新数据,版本号会自动+1user.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 {@Autowiredprivate UserMapper userMapper;@Autowiredprivate OrderService orderService;/*** 分布式事务场景下的用户创建*/@GlobalTransactionalpublic void createUserWithOrder(User user, Order order) {// 创建用户userMapper.insert(user);// 调用订单服务orderService.createOrder(order);}
}
缓存集成与性能优化
// Redis缓存集成
@Service
public class CachedUserService extends ServiceImpl<UserMapper, User> {@Autowiredprivate 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();}@Beanpublic 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 {@Autowiredprivate 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过程中的经验和遇到的问题。
技术无止境,学习永不停! 🚀
