当前位置: 首页 > news >正文

【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 最佳实践建议

性能优化建议

  1. 合理使用分页查询,避免大数据量查询
  2. 启用SQL日志,监控慢查询并优化
  3. 使用连接池监控,及时发现连接泄露
  4. 合理设置缓存策略,提高查询性能

代码规范建议

  1. 统一实体类设计,继承审计基类
  2. 规范命名约定,保持代码可读性
  3. 合理使用条件构造器,避免复杂的嵌套查询
  4. 编写单元测试,保证代码质量

安全性建议

  1. 启用逻辑删除,保护重要数据
  2. 使用乐观锁,处理并发更新
  3. 参数校验,防止SQL注入
  4. 敏感字段加密,保护用户隐私

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 - 参加线下技术分享活动
  • 博客和视频分享 - 分享自己的学习心得和实战经验

持续学习建议

  1. 关注官方动态 - 及时了解新版本特性和更新
  2. 实践项目应用 - 在实际项目中应用所学知识
  3. 参与开源贡献 - 为开源项目贡献代码和文档
  4. 分享学习心得 - 通过博客、视频等方式分享经验

结语

Mybatis-Plus作为优秀的持久层增强工具,极大地提升了Java开发者的工作效率。通过本文的深入学习,相信你已经掌握了从基础配置到高级特性的全面知识。

技术的学习是一个持续的过程,希望这篇文章能够成为你Mybatis-Plus学习路上的重要参考。在实际项目中,要根据具体业务需求选择合适的技术方案,在便利性和性能之间找到最佳平衡点。

如果这篇文章对你有帮助,请不要忘记:

  • 👍 点赞支持 - 你的认可是我继续创作的动力
  • 收藏文章 - 方便日后查阅和复习
  • 🔄 转发分享 - 让更多的开发者受益
  • 💬 评论交流 - 分享你的学习心得和实战经验
  • 🔔 关注更新 - 获取更多优质技术内容

让我们一起在技术的道路上不断前行,用代码改变世界!期待在评论区看到你的想法和建议,也欢迎分享你在使用Mybatis-Plus过程中的经验和遇到的问题。

技术无止境,学习永不停! 🚀

http://www.dtcms.com/a/539977.html

相关文章:

  • LangGraph 官方教程:聊天机器人之五
  • 天硕工业SSD揭秘无DRAM缓存SSD的性能差距
  • C# 内存是绝对自动清理吗?
  • 在 CentOS 系统上实现定时执行 Python 邮件发送任务完整指南
  • C#操作Excel
  • 放置在网站根目录下中国做外贸最好的网站有哪些
  • 二叉搜索树,咕咕咕
  • 可用 Docker (DockerHub) 国内镜像源加速列表 - 长期维护(截至 2025 年 06 月 15 日)
  • QtQuick3D入门(5):实例化渲染
  • 浙人医基于金仓 KFS 工具信创落地:多数据库协同难题解决方案详讲
  • [C++STL] :list的简介和使用
  • Nacos配置中心实战进阶:多场景动态刷新全解析
  • Linux写sh开机启动脚本-bash报错的两种解决方法
  • 注册协议通知
  • wordpress网站部署百度一下一下你就知道
  • 健康濮阳门户网站建设装企erp管理系统
  • C++ stack和queue之OJ题目
  • 【网络】在windows下,使用自带的ftp服务器,并添加账户
  • 基于python大数据的网络新闻可视化及分析系统
  • 6.1.1.3 大数据方法论与实践指南-SparkStreaming 任务优化实践
  • uniapp实现PDF的预览
  • 推送远程git仓库报错:内部服务错误
  • Qt 6以上版本都试用 连接 MySQL 数据库全流程(CMake 环境)
  • 使用 C# 打印 PDF 文档:基于 Spire.PDF 的实战教程
  • 数据库--JDBC编程
  • 开源一个基于OpenCV的模糊检测工具,支持局部分析和视频处理
  • 政协网站建设情况汇报为什么wordpress安装成了英文版
  • 不做网站只做推广可以么襄阳网站建设首选公司哪家好
  • 10月28日
  • 【加精】C# XML差异对比 (直接用)