Repository: fengwenyi/MyBatis-Plus-Example Branch: master Commit: a665d5a94c50 Files: 46 Total size: 49.8 KB Directory structure: gitextract_ke3bd0xo/ ├── .gitignore ├── README.md ├── doc/ │ ├── 00-mybatis-plus知识点.md │ ├── 01-SpringBoot整合MyBatis-Plus.md │ ├── 02-mybatis-plus-example.sql │ ├── 03-MyBatis-Plus代码生成器的使用.md │ ├── 04-CRUD.md │ ├── 05-mybatis知识点.md │ ├── 06-MyBatis-Plus条件查询.md │ ├── 07-MyBatis-Plus分页查询.md │ ├── 08-MyBatis-Plus数据库枚举实现.md │ ├── 09-MyBatis-Plus乐观锁.md │ ├── 10-MyBatis-Plus多数据源(动态).md │ ├── 11-MyBatis-Plus连表查询.md │ ├── 12-使用p6spy-SQL打印和性能分析.md │ ├── 13-MyBatis-Plus逻辑删除.md │ ├── 14-MyBatis-Plus数据安全保护.md │ ├── 15-MyBatis-Plus字段填充.md │ └── version/ │ └── 3.2.1.md ├── pom.xml └── src/ ├── main/ │ ├── java/ │ │ └── com/ │ │ └── fengwenyi/ │ │ └── mybatisplusexample/ │ │ ├── MybatisPlusExampleApplication.java │ │ ├── config/ │ │ │ └── MyBatisPlusConfiguration.java │ │ ├── entity/ │ │ │ ├── CategoryEntity.java │ │ │ ├── GoodsEntity.java │ │ │ └── enums/ │ │ │ └── GoodsFlagEnum.java │ │ ├── handler/ │ │ │ └── MyBatisPlusAutoFillMetaObjectHandler.java │ │ ├── mapper/ │ │ │ ├── ICategoryMapper.java │ │ │ └── IGoodsMapper.java │ │ └── repository/ │ │ ├── MPCategoryRepository.java │ │ ├── MPGoodsRepository.java │ │ └── impl/ │ │ ├── CategoryRepositoryImpl.java │ │ └── GoodsRepositoryImpl.java │ └── resources/ │ ├── application-dev.yml │ ├── application-prod.yml │ ├── application-test.yml │ ├── application.yml │ ├── logback-spring.xml │ ├── mapper/ │ │ ├── CategoryMapper.xml │ │ └── GoodsMapper.xml │ └── spy.properties └── test/ └── java/ └── com/ └── fengwenyi/ └── mybatisplusexample/ ├── CategoryMapperTests.java ├── CategoryModelTests.java ├── CategoryRepositoryTests.java ├── GoodsRepositoryTests.java ├── MPWTests.java └── MybatisPlusExampleApplicationTests.java ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ HELP.md /target/ .mvn/ mvnw mvnw.cmd ### STS ### .apt_generated .classpath .factorypath .project .settings .springBeans .sts4-cache ### IntelliJ IDEA ### .idea *.iws *.iml *.ipr ### NetBeans ### /nbproject/private/ /nbbuild/ /dist/ /nbdist/ /.nb-gradle/ /build/ ### VS Code ### .vscode/ /log/ .gradle gradle gradlew gradlew.bat *.log ================================================ FILE: README.md ================================================ # MyBatis-Plus-Example Spring Boot 整合 MyBatis Plus 实例,持续更新!!! ## 代码生成器 [请点击此处的传送门](https://github.com/fengwenyi/mybatis-plus-code-generator) ## 不再维护 作者尽力有限,此项目不再维护,后续会全部迁移到这里 [demo-spring-boot-mybatis-plus](https://github.com/fengwenyi/spring-boot-demo/tree/main/demo-spring-boot-mybatis-plus) ## 三方框架版本总览 | 名称 | 版本 | 更新时间 | | --- | --- | --- | | Spring Boot | 2.4.2 | 2021.02.02 | | MyBatis-Plus | 3.4.2 | 2021.02.02 | | JavaLib | 2.1.1 | 2021.07.06 | | api-result | 2.3.1 | 2021.02.02 | | swagger | 2.9.2 | 2019.08.27 | ## 项目版本标识说明 BUILD 开发版本:用于标识该版本正在构建或者开发中。 SNAPSHOT 预览版本:开发已经完成,开始进入测试阶段。 RELEASE 稳定版本:已发布到中央仓库。 ## 数据库设计 ### 商品类别表-category | 字段 | 类型 | 说明 | | --- | --- | --- | | id | bigint(20) | 主键ID | | name | varchar(50) | 类别名称 | ### 商品表-goods | 字段 | 类型 | 说明 | | --- | --- | --- | | id | bigint(20) | 主键ID | | name | varchar(255) | 商品名称 | | category_id | bigint(20) | 类别ID | | stock_num | bigint(20) | 库存数量 | | price | decimal(9, 4) | 单价 | | flag | tinyint(1) | 上下架。0:下架;1:上架。默认0。 | | delete_status | tinyint(1) | 逻辑删除状态。0:正常;1:删除。默认0。 | | create_time | datetime | 创建时间 | | update_time | datetime | 更新时间 | | create_by | varchar(64) | 创建人 | | update_by | varchar(64) | 修改人 | | version | int(11) | 版本。默认:0。 | ## 常见问题 ### 1、LocalDateTime无法使用的问题 #### 描述 用代码生成器生成的时间类型默认为 `LocalDateTime`, 但是在项目中使用报错。今天在项目上遇到了这个问题,什么原因呢? #### 分析 首先要明确,如果你的项目都和示例项目配置、依赖、版本都一样,那说明本身是没有问题的。肯定是其他问题导致的。 经过排查,项目引入了 `druid`, 版本比较老,所以无法转换导致的。 #### 解决 升级 `druid` 版本 ================================================ FILE: doc/00-mybatis-plus知识点.md ================================================ # MyBatis-Plus知识点 ## Spring Boot整合MyBatis-Plus 第一步:pom.xml引入MyBatis-Plus依赖,注意,不需要再引入MyBatis的包,因为我这里使用Spring Boot搭建的工程,所有因为方式见下: ```xml ... com.baomidou mybatis-plus-boot-starter 3.0.1 ... ``` 第二步:将生成的代码,拷贝到相应的包下 ![代码目录结构](https://upload-images.jianshu.io/upload_images/5805596-368a8af54fe639b7.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 第三步:在配置文件中进行相应的配置 具体配置可参考官网,这里需要注意这样几个地方: ```yaml mybatis-plus: # xml mapper-locations: classpath:mapper/*Mapper.xml # 实体扫描,多个package用逗号或者分号分隔 type-aliases-package: com.fengwenyi.mp3demo.model configuration: # 这个配置会将执行的sql打印出来,在开发或测试的时候可以用 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl ``` ![日志:分页查询](https://upload-images.jianshu.io/upload_images/5805596-f5b314a660a48844.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 第四步:在启动类上添加下面的注解 ```java @EnableTransactionManagement @MapperScan("com.fengwenyi.mp3demo.dao") ``` ## 增删改 **Service** 我们一起去看源码 `com.baomidou.mybatisplus.extension.service.IService` 增加: ``` /** *

* 插入一条记录(选择字段,策略插入) *

* * @param entity 实体对象 */ boolean save(T entity); ``` 修改: ```java /** *

* 根据 ID 选择修改 *

* * @param entity 实体对象 */ boolean updateById(T entity); /** *

* 根据 whereEntity 条件,更新记录 *

* * @param entity 实体对象 * @param updateWrapper 实体对象封装操作类 * {@link com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper} */ boolean update(T entity, Wrapper updateWrapper); ``` 删除: ```java /** *

* 根据 ID 删除 *

* * @param id 主键ID */ boolean removeById(Serializable id); /** *

* 根据 entity 条件,删除记录 *

* * @param queryWrapper 实体包装类 * {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper} */ boolean remove(Wrapper queryWrapper); ``` **Mapper** `com.baomidou.mybatisplus.core.mapper.BaseMapper` 增加: ```java /** *

* 插入一条记录 *

* * @param entity 实体对象 */ int insert(T entity); ``` 修改: ```java /** *

* 根据 whereEntity 条件,更新记录 *

* * @param entity 实体对象 (set 条件值,不能为 null) * @param updateWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句) */ int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper updateWrapper); /** *

* 根据 ID 修改 *

* * @param entity 实体对象 */ int updateById(@Param(Constants.ENTITY) T entity); ``` 删除: ```java /** *

* 根据 entity 条件,删除记录 *

* * @param queryWrapper 实体对象封装操作类(可以为 null) */ int delete(@Param(Constants.WRAPPER) Wrapper queryWrapper); /** *

* 根据 ID 删除 *

* * @param id 主键ID */ int deleteById(Serializable id); ``` 以上相当于是常用API了,我们去看看,他是怎么实现的。毫无疑问,Mapper是底层,Service调用Mapper去执行sql,完成相关操作,所以,你完全可以直接调用Mapper完成相关操作,就跟使用MyBatis一样。下面我们去看看,他帮我们写的Service是什么样子,这里只看一个修改操作吧。 接口: ```java /** *

* 根据 whereEntity 条件,更新记录 *

* * @param entity 实体对象 * @param updateWrapper 实体对象封装操作类 * {@link com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper} */ boolean update(T entity, Wrapper updateWrapper); ``` 实现: ```java @Override public boolean update(T entity, Wrapper updateWrapper) { return ServiceImpl.retBool(baseMapper.update(entity, updateWrapper)); } /** *

* 判断数据库操作是否成功 *

*

* 注意!! 该方法为 Integer 判断,不可传入 int 基本类型 *

* * @param result 数据库操作返回影响条数 * @return boolean */ protected static boolean retBool(Integer result) { return SqlHelper.retBool(result); } /** *

* 判断数据库操作是否成功 *

* * @param result 数据库操作返回影响条数 * @return boolean */ public static boolean retBool(Integer result) { return null != result && result >= 1; } ``` 哈哈,是不是我们自己也会这样写啊! ## 查询 接下来,我们一起讨论下查询吧。 MP 3.x,查询接口发生了很大的变化,反正我是不喜欢的,你就弄一个什么开头啊,到时候,我一点就知道有哪些方法了,他这里有 `list*`, `get*`,反正就是一个字——没必要。 先看下接口说明: ```java /** *

* 查询列表 *

* * @param queryWrapper 实体对象封装操作类 * {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper} */ List list(Wrapper queryWrapper); /** *

* 根据 ID 查询 *

* * @param id 主键ID */ T getById(Serializable id); /** *

* 根据 Wrapper,查询一条记录 *

* * @param queryWrapper 实体对象封装操作类 * {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper} */ T getOne(Wrapper queryWrapper); ``` 嗯,差不多了吧,这样需要注意这样一个方法: ```java /** *

* 从list中取第一条数据返回对应List中泛型的单个结果 *

* * @param list * @param * @return */ public static E getObject(List list) { if (CollectionUtils.isNotEmpty(list)) { int size = list.size(); if (size > 1) { SqlHelper.logger.warn( String.format("Warn: execute Method There are %s results.", size)); } return list.get(0); } return null; } ``` **下面说下分页的问题** 根据官网的说法,需要借助插件,这我们是可以理解。 在Spring Boot启动类里面添加: ```java /** * 分页插件 */ @Bean public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); } ``` 这样就可以使用他提供的分页接口了: ```java /** *

* 翻页查询 *

* * @param page 翻页对象 * @param queryWrapper 实体对象封装操作类 * {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper} */ IPage page(IPage page, Wrapper queryWrapper); ``` 我们去看一下: ```java @Override public IPage page(IPage page, Wrapper queryWrapper) { queryWrapper = (Wrapper) SqlHelper.fillWrapper(page, queryWrapper); return baseMapper.selectPage(page, queryWrapper); } /** *

* 填充Wrapper *

* * @param page 分页对象 * @param wrapper SQL包装对象 */ @SuppressWarnings("unchecked") public static Wrapper fillWrapper(IPage page, Wrapper wrapper) { if (null == page) { return wrapper; } if (ArrayUtils.isEmpty(page.ascs()) && ArrayUtils.isEmpty(page.descs()) && ObjectUtils.isEmpty(page.condition())) { return wrapper; } QueryWrapper qw; if (null == wrapper) { qw = new QueryWrapper<>(); } else { qw = (QueryWrapper) wrapper; } // 排序 if (ArrayUtils.isNotEmpty(page.ascs())) { qw.orderByAsc(page.ascs()); } if (ArrayUtils.isNotEmpty(page.descs())) { qw.orderByDesc(page.descs()); } // MAP 参数查询 if (ObjectUtils.isNotEmpty(page.condition())) { qw.allEq(page.condition()); } return qw; } /** *

* 根据 entity 条件,查询全部记录(并翻页) *

* * @param page 分页查询条件(可以为 RowBounds.DEFAULT) * @param queryWrapper 实体对象封装操作类(可以为 null) */ IPage selectPage(IPage page, @Param(Constants.WRAPPER) Wrapper queryWrapper); ``` 分页的代码大抵就是这样,我之前也自己写过,思路还是相当来说比较简单,关键是看你的查询添加如何封装,分页类如何构造。 这里有一点说明: 分页从 **`1`** 开始 !!! ## 枚举类 1、实现 接口 ```java /** *

* 自定义枚举接口 *

* * @author hubin * @since 2017-10-11 */ public interface IEnum { /** * 枚举数据库存储值 */ T getValue(); } ``` 2、实现注意 ``` @Override public Integer getValue() { return this.value; } @JsonValue public String getDesc() { return desc; } ``` 这是Jackson的写法,我没用FastJson,所以用的伙伴,去官网看一下:[FastJson看官网](http://mp.baomidou.com/#/enum?id=%E4%BA%8C%E3%80%81fastjson)。 3:被忘了在配置文件中添加扫描: ```yaml mybatis-plus: # 扫描枚举类 # 支持统配符 * 或者 ; 分割 type-enums-package: com.fengwenyi.mp3demo.enums ``` 差不多了吧,好像 ## 逻辑删除 1、代码生成器中配置: ```java new StrategyConfig().setLogicDeleteFieldName("is_delete") // 逻辑删除属性名称 ``` 或者,你可以手写,参考: ```java @ApiModelProperty(value = "是否逻辑删除(true:删除;false:正常(默认))") @TableLogic private Boolean isDelete; ``` 2、自定义数据库的值: ```yaml mybatis-plus: global-config: db-config: #逻辑删除配置 logic-delete-value: 1 logic-not-delete-value: 0 ``` ## 逻辑删除 ## 乐观锁 ## SQL性能分析 ## MyBatis-Plus-Example MyBatis-Plus的代码都会上传到github上 https://github.com/fengwenyi/MyBatis-Plus-Example ## 参考资料 * [MyBatis-Plus](http://mp.baomidou.com/#/) * [MyBatis-Plus 使用枚举自动关联注入](https://www.imooc.com/article/details/id/29760) * [mybatis-plus插件使用的一些问题](https://www.jianshu.com/p/a5c9bab9584a) * [设计模式之Builder模式](https://www.jianshu.com/p/e2a2fe3555b9) * [修复Long类型太长,而Java序列化JSON丢失精度问题的方法](https://www.jianshu.com/p/fbcdcfc7cd12) ================================================ FILE: doc/01-SpringBoot整合MyBatis-Plus.md ================================================ ================================================ FILE: doc/02-mybatis-plus-example.sql ================================================ /* Navicat Premium Data Transfer Source Server : localhost.01-192.168.16.128 Source Server Type : MySQL Source Server Version : 80021 Source Host : 192.168.16.128:3306 Source Schema : mybatis-plus-example Target Server Type : MySQL Target Server Version : 80021 File Encoding : 65001 Date: 07/03/2021 18:15:28 */ SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for shop_category -- ---------------------------- DROP TABLE IF EXISTS `shop_category`; CREATE TABLE `shop_category` ( `id` bigint(0) NOT NULL COMMENT '主键ID', `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '商品类别名称', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = Dynamic; -- ---------------------------- -- Table structure for shop_goods -- ---------------------------- DROP TABLE IF EXISTS `shop_goods`; CREATE TABLE `shop_goods` ( `id` bigint(0) NOT NULL COMMENT '主键ID', `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '商品名称', `category_id` bigint(0) DEFAULT NULL COMMENT '商品类别ID', `stock_num` bigint(0) DEFAULT NULL COMMENT '库存数量', `price` decimal(9, 4) DEFAULT NULL COMMENT '商品单价', `flag` tinyint(1) DEFAULT NULL COMMENT '上下架。0:下架;1:上架。默认0。', `delete_status` tinyint(1) DEFAULT NULL COMMENT '逻辑删除状态。0:正常;1:删除。默认0。', `create_time` datetime(0) DEFAULT NULL COMMENT '创建时间', `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '创建人', `update_time` datetime(0) DEFAULT NULL COMMENT '更新时间', `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '修改人', `version` int(0) DEFAULT NULL COMMENT '版本。默认:0。', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = Dynamic; SET FOREIGN_KEY_CHECKS = 1; ================================================ FILE: doc/03-MyBatis-Plus代码生成器的使用.md ================================================ ================================================ FILE: doc/04-CRUD.md ================================================ # CRUD - 增加(Create) - 检索(Retrieve) - 更新(Update) - 删除(Delete) 对于MyBatis-Plus而言,增删改查,有三种方式实现。 1、使用MyBatis-Plus提供的服务接口。 2、使用MyBatis的Mapper接口。 3、使用Mybatis-Plus提供的AR特性,Model可进行CRUD操作。 AR即为ActiveRecord,是一种领域模型模式,一个模型类对应一个表。通过实体类对象直接进行表的CRUD操作。 ## MyBatis-Plus服务接口 [CategoryRepositoryTests](../src/test/java/com.fengwenyi.mybatisplusexample.CategoryRepositoryTests) ### 数据添加 ``` mpXxxService.save(entity); ``` ### 数据删除 ``` mpXxxService.removeById(entity.getId()); ``` ### 数据查询 ``` mpXxxService.list(queryWrapper); ``` ### 数据修改 ``` mpXxxService.updateById(entity); ``` ## MyBatis数据库操作Mapper接口 [CategoryMapperTests](../src/test/java/com.fengwenyi.mybatisplusexample.CategoryMapperTests) ### 数据添加 ``` xxxMapper.insert(entity); ``` ### 数据删除 ``` xxxMapper.deleteById(entity); ``` ### 数据查询 ``` xxxMapper.selectList(queryWrapper); ``` ### 数据修改 ``` xxxMapper.updateById(entity); ``` ## Mybatis-Plus提供的AR [CategoryModelTests](../src/test/java/com.fengwenyi.mybatisplusexample.CategoryModelTests) ### 数据添加 ``` xxxModel.insert(); ``` ### 数据删除 ``` xxxModel.deleteById(); ``` ### 数据修改 ``` xxxModel.updateById(); ``` ================================================ FILE: doc/05-mybatis知识点.md ================================================ ================================================ FILE: doc/06-MyBatis-Plus条件查询.md ================================================ # MyBatis-Plus条件查询 ## Wrapper ### QueryWrapper 查询条件构造器 ### LambdaQueryWrapper Lambda的使用 ### LambdaQueryChainWrapper 链式查询 ================================================ FILE: doc/07-MyBatis-Plus分页查询.md ================================================ ================================================ FILE: doc/08-MyBatis-Plus数据库枚举实现.md ================================================ # 枚举 ================================================ FILE: doc/09-MyBatis-Plus乐观锁.md ================================================ # 乐观锁 添加拦截器 ``` interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); // 乐观锁插件 ``` 乐观锁字段注解: ``` @Version ``` ================================================ FILE: doc/10-MyBatis-Plus多数据源(动态).md ================================================ ================================================ FILE: doc/11-MyBatis-Plus连表查询.md ================================================ ================================================ FILE: doc/12-使用p6spy-SQL打印和性能分析.md ================================================ ================================================ FILE: doc/13-MyBatis-Plus逻辑删除.md ================================================ ================================================ FILE: doc/14-MyBatis-Plus数据安全保护.md ================================================ ================================================ FILE: doc/15-MyBatis-Plus字段填充.md ================================================ # 自动填充 @TableField(.. fill = FieldFill.INSERT) ``` /** * 默认不处理 */ DEFAULT, /** * 插入填充字段 */ INSERT, /** * 更新填充字段 */ UPDATE, /** * 插入和更新填充字段 */ INSERT_UPDATE ``` 实现元对象处理器接口:com.baomidou.mybatisplus.core.handlers.MetaObjectHandler ``` 注意事项: - 填充原理是直接给entity的属性设置值!!! - 注解则是指定该属性在对应情况下必有值,如果无值则入库会是null - MetaObjectHandler提供的默认方法的策略均为:如果属性有值则不覆盖,如果填充值为null则不填充 - 字段必须声明TableField注解,属性fill选择对应策略,该声明告知Mybatis-Plus需要预留注入SQL字段 - 填充处理器MyMetaObjectHandler在 Spring Boot 中需要声明@Component或@Bean注入 - 要想根据注解FieldFill.xxx和字段名以及字段类型来区分必须使用父类的strictInsertFill或者strictUpdateFill方法 - 不需要根据任何来区分可以使用父类的fillStrategy方法 ``` 还需要再研究研究 ================================================ FILE: doc/version/3.2.1.md ================================================ # 3.2.1 | 项 | 说明 | | --- | --- | | 构建时间 | 2021.02.02 | | 发布时间 | | | 版本号 | 3.2.1 | ## 新特性 ## 优化 ## 删除 ## 文档 ## 开发者 ================================================ FILE: pom.xml ================================================ 4.0.0 org.springframework.boot spring-boot-starter-parent 2.4.2 com.fengwenyi MyBatis-Plus-Example 3.2.1-BUILD jar MyBatis-Plus-Example Spring Boot 整合 MyBatis Plus 实例 1.8 2.9.2 2.1.1 3.4.2 1.2.59 2.3.1 org.springframework.boot spring-boot-starter-web mysql mysql-connector-java runtime org.projectlombok lombok true org.springframework.boot spring-boot-starter-test test com.baomidou mybatis-plus-boot-starter ${mybatis-plus.version} io.springfox springfox-swagger2 ${swagger2.version} io.springfox springfox-swagger-ui ${swagger2.version} com.fengwenyi JavaLib ${JavaLib.version} com.fengwenyi api-result ${api-result.version} p6spy p6spy 3.9.1 org.springframework.boot spring-boot-maven-plugin ================================================ FILE: src/main/java/com/fengwenyi/mybatisplusexample/MybatisPlusExampleApplication.java ================================================ package com.fengwenyi.mybatisplusexample; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class MybatisPlusExampleApplication { public static void main(String[] args) { SpringApplication.run(MybatisPlusExampleApplication.class, args); } } ================================================ FILE: src/main/java/com/fengwenyi/mybatisplusexample/config/MyBatisPlusConfiguration.java ================================================ package com.fengwenyi.mybatisplusexample.config; import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @author Erwin Feng * @since 2021-03-08 */ @Configuration @MapperScan("com.fengwenyi.mybatisplusexample.mapper") public class MyBatisPlusConfiguration { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); // 分页插件 interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); // 乐观锁插件 return interceptor; } } ================================================ FILE: src/main/java/com/fengwenyi/mybatisplusexample/entity/CategoryEntity.java ================================================ package com.fengwenyi.mybatisplusexample.entity; import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.extension.activerecord.Model; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableField; import java.io.Serializable; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.Accessors; /** *

* *

* * @author Erwin Feng * @since 2021-03-07 */ @Data @EqualsAndHashCode(callSuper = false) @Accessors(chain = true) @TableName("shop_category") public class CategoryEntity extends Model { private static final long serialVersionUID = 1L; /** * 主键ID */ @TableId(value = "id", type = IdType.ASSIGN_ID) private Long id; /** * 商品类别名称 */ @TableField("name") private String name; @Override protected Serializable pkVal() { return this.id; } } ================================================ FILE: src/main/java/com/fengwenyi/mybatisplusexample/entity/GoodsEntity.java ================================================ package com.fengwenyi.mybatisplusexample.entity; import java.math.BigDecimal; import com.baomidou.mybatisplus.annotation.*; import com.baomidou.mybatisplus.extension.activerecord.Model; import java.time.LocalDateTime; import java.io.Serializable; import com.fengwenyi.mybatisplusexample.entity.enums.GoodsFlagEnum; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.Accessors; /** *

* *

* * @author Erwin Feng * @since 2021-03-07 */ @Data @EqualsAndHashCode(callSuper = false) @Accessors(chain = true) @TableName("shop_goods") public class GoodsEntity extends Model { private static final long serialVersionUID = 1L; /** * 主键ID */ @TableId(value = "id", type = IdType.ASSIGN_ID) private Long id; /** * 商品名称 */ @TableField("name") private String name; /** * 商品类别ID */ @TableField("category_id") private Long categoryId; /** * 库存数量 */ @TableField("stock_num") private Long stockNum; /** * 商品单价 */ @TableField("price") private BigDecimal price; /** * 上下架。0:下架;1:上架。默认0。 */ @TableField("flag") private GoodsFlagEnum flag; /** * 逻辑删除状态。0:正常;1:删除。默认0。 */ @TableField("delete_status") @TableLogic private Boolean deleteStatus; /** * 创建时间 */ @TableField(value = "create_time", fill = FieldFill.INSERT) private LocalDateTime createTime; /** * 创建人 */ @TableField(value = "create_by", fill = FieldFill.INSERT) private String createBy; /** * 更新时间 */ @TableField(value = "update_time", fill = FieldFill.UPDATE) private LocalDateTime updateTime; /** * 修改人 */ @TableField(value = "update_by", fill = FieldFill.UPDATE) private String updateBy; /** * 版本。默认:0。 */ @TableField("version") @Version private Integer version; @Override protected Serializable pkVal() { return this.id; } } ================================================ FILE: src/main/java/com/fengwenyi/mybatisplusexample/entity/enums/GoodsFlagEnum.java ================================================ package com.fengwenyi.mybatisplusexample.entity.enums; import com.baomidou.mybatisplus.annotation.IEnum; import lombok.Getter; /** * @author Erwin Feng * @since 2021-03-08 */ @Getter public enum GoodsFlagEnum implements IEnum { DOWN(0, "下架"), UP(1, "上架"), ; private final Integer value; private final String desc; GoodsFlagEnum(Integer value, String desc) { this.value = value; this.desc = desc; } @Override public Integer getValue() { return this.value; } } ================================================ FILE: src/main/java/com/fengwenyi/mybatisplusexample/handler/MyBatisPlusAutoFillMetaObjectHandler.java ================================================ package com.fengwenyi.mybatisplusexample.handler; import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.reflection.MetaObject; import org.springframework.stereotype.Component; import java.time.LocalDateTime; /** * @author Erwin Feng * @since 2021-04-01 */ @Slf4j @Component public class MyBatisPlusAutoFillMetaObjectHandler implements MetaObjectHandler { @Override public void insertFill(MetaObject metaObject) { metaObject.setValue("createTime", LocalDateTime.now()); // log.info("start insert fill ...."); // this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐使用) // 或者 // this.strictUpdateFill(metaObject, "createTime", () -> LocalDateTime.now(), LocalDateTime.class); // 起始版本 3.3.3(推荐) // 或者 // this.fillStrategy(metaObject, "createTime", LocalDateTime.now()); // 也可以使用(3.3.0 该方法有bug) } @Override public void updateFill(MetaObject metaObject) { metaObject.setValue("updateTime", LocalDateTime.now()); // log.info("start update fill ...."); // this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐) // 或者 // this.strictUpdateFill(metaObject, "updateTime", LocalDateTime::now, LocalDateTime.class); // 起始版本 3.3.3(推荐) // 或者 // this.fillStrategy(metaObject, "updateTime", LocalDateTime.now()); // 也可以使用(3.3.0 该方法有bug) } } ================================================ FILE: src/main/java/com/fengwenyi/mybatisplusexample/mapper/ICategoryMapper.java ================================================ package com.fengwenyi.mybatisplusexample.mapper; import com.fengwenyi.mybatisplusexample.entity.CategoryEntity; import com.baomidou.mybatisplus.core.mapper.BaseMapper; /** *

* Mapper 接口 *

* * @author Erwin Feng * @since 2021-03-07 */ public interface ICategoryMapper extends BaseMapper { } ================================================ FILE: src/main/java/com/fengwenyi/mybatisplusexample/mapper/IGoodsMapper.java ================================================ package com.fengwenyi.mybatisplusexample.mapper; import com.fengwenyi.mybatisplusexample.entity.GoodsEntity; import com.baomidou.mybatisplus.core.mapper.BaseMapper; /** *

* Mapper 接口 *

* * @author Erwin Feng * @since 2021-03-07 */ public interface IGoodsMapper extends BaseMapper { } ================================================ FILE: src/main/java/com/fengwenyi/mybatisplusexample/repository/MPCategoryRepository.java ================================================ package com.fengwenyi.mybatisplusexample.repository; import com.fengwenyi.mybatisplusexample.entity.CategoryEntity; import com.baomidou.mybatisplus.extension.service.IService; /** *

* 服务类 *

* * @author Erwin Feng * @since 2021-03-07 */ public interface MPCategoryRepository extends IService { } ================================================ FILE: src/main/java/com/fengwenyi/mybatisplusexample/repository/MPGoodsRepository.java ================================================ package com.fengwenyi.mybatisplusexample.repository; import com.fengwenyi.mybatisplusexample.entity.GoodsEntity; import com.baomidou.mybatisplus.extension.service.IService; /** *

* 服务类 *

* * @author Erwin Feng * @since 2021-03-07 */ public interface MPGoodsRepository extends IService { } ================================================ FILE: src/main/java/com/fengwenyi/mybatisplusexample/repository/impl/CategoryRepositoryImpl.java ================================================ package com.fengwenyi.mybatisplusexample.repository.impl; import com.fengwenyi.mybatisplusexample.entity.CategoryEntity; import com.fengwenyi.mybatisplusexample.mapper.ICategoryMapper; import com.fengwenyi.mybatisplusexample.repository.MPCategoryRepository; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.springframework.stereotype.Service; /** *

* 服务实现类 *

* * @author Erwin Feng * @since 2021-03-07 */ @Service public class CategoryRepositoryImpl extends ServiceImpl implements MPCategoryRepository { } ================================================ FILE: src/main/java/com/fengwenyi/mybatisplusexample/repository/impl/GoodsRepositoryImpl.java ================================================ package com.fengwenyi.mybatisplusexample.repository.impl; import com.fengwenyi.mybatisplusexample.entity.GoodsEntity; import com.fengwenyi.mybatisplusexample.mapper.IGoodsMapper; import com.fengwenyi.mybatisplusexample.repository.MPGoodsRepository; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.springframework.stereotype.Service; /** *

* 服务实现类 *

* * @author Erwin Feng * @since 2021-03-07 */ @Service public class GoodsRepositoryImpl extends ServiceImpl implements MPGoodsRepository { } ================================================ FILE: src/main/resources/application-dev.yml ================================================ #spring spring: datasource: # driver-class-name: com.mysql.jdbc.Driver # url: jdbc:mysql://192.168.16.128:3306/mybatis-plus-example username: root # password: kU#m5eHY5iTiQj#q password: 123456 driver-class-name: com.p6spy.engine.spy.P6SpyDriver url: jdbc:p6spy:mysql://192.168.16.128:3306/mybatis-plus-example # url: mpw:pBZqJb+r/StFGyRopKJyhZN8dvstxaXxubewECBMWM3e7XK+719AUXlC1y1Cu14i1MwqEdiJbBa+sEwFzyWEgA== # username: mpw:hjR022j+cjKFE+35FLTQgg== # password: mpw:kzLGIBmi4hT+dPmqnXLAIw== mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl logback: logDir: log/dev logging: level: com.fengwenyi.mybatis_plus_example.service.impl: trace com.fengwenyi.mybatis_plus_example.controller: trace ================================================ FILE: src/main/resources/application-prod.yml ================================================ ================================================ FILE: src/main/resources/application-test.yml ================================================ ================================================ FILE: src/main/resources/application.yml ================================================ #app server: port: 8080 spring: application: name: mybatis-plus-example profiles: active: dev #mybatis mybatis-plus: # xml mapper-locations: classpath:mapper/*Mapper.xml # 实体扫描,多个package用逗号或者分号分隔 type-aliases-package: com.fengwenyi.mybatisplusexample.entity # 扫描枚举类 # 支持统配符 * 或者 ; 分割 type-enums-package: com.fengwenyi.mybatisplusexample.entity.enums global-config: db-config: #数据库大写下划线转换 capital-mode: true #逻辑删除配置 # logic-delete-field: delete_status # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2) logic-delete-value: 1 # 逻辑删除 logic-not-delete-value: 0 # 正常 configuration: map-underscore-to-camel-case: true cache-enabled: false default-enum-type-handler: org.apache.ibatis.type.EnumOrdinalTypeHandler logback: appName: mybatis-plus-example fileType: log ================================================ FILE: src/main/resources/logback-spring.xml ================================================ ${CONSOLE_LOG_PATTERN} UTF-8 debug ${logDir}/${appName}-debug.${fileType} %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n UTF-8 ${logDir}/${appName}-debug-%d{yyyy-MM-dd}.%i.${fileType} 500MB 30 debug ACCEPT DENY ${logDir}/${appName}-info.${fileType} %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n UTF-8 ${logDir}/${appName}-info-%d{yyyy-MM-dd}.%i.${fileType} 500MB 30 info ACCEPT DENY ${logDir}/${appName}-warn.${fileType} %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n UTF-8 ${logDir}/${appName}-warn-%d{yyyy-MM-dd}.%i.${fileType} 500MB 30 warn ACCEPT DENY ${logDir}/${appName}-error.${fileType} %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n UTF-8 ${logDir}/${appName}-error-%d{yyyy-MM-dd}.%i.${fileType} 500MB 30 error ACCEPT DENY ================================================ FILE: src/main/resources/mapper/CategoryMapper.xml ================================================ ================================================ FILE: src/main/resources/mapper/GoodsMapper.xml ================================================ ================================================ FILE: src/main/resources/spy.properties ================================================ #3.2.1以上使用 modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory #3.2.1以下使用或者不配置 #modulelist=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory # 自定义日志打印 logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger #日志输出到控制台 appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger # 使用日志系统记录 sql #appender=com.p6spy.engine.spy.appender.Slf4JLogger # 设置 p6spy driver 代理 deregisterdrivers=true # 取消JDBC URL前缀 useprefix=true # 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset. excludecategories=info,debug,result,commit,resultset # 日期格式 dateformat=yyyy-MM-dd HH:mm:ss # 实际驱动可多个 #driverlist=org.h2.Driver # 是否开启慢SQL记录 outagedetection=true # 慢SQL记录标准 2 秒 outagedetectioninterval=2 ================================================ FILE: src/test/java/com/fengwenyi/mybatisplusexample/CategoryMapperTests.java ================================================ package com.fengwenyi.mybatisplusexample; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.fengwenyi.mybatisplusexample.entity.CategoryEntity; import com.fengwenyi.mybatisplusexample.mapper.ICategoryMapper; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Test; import org.springframework.stereotype.Component; import org.springframework.util.Assert; import javax.annotation.Resource; import java.util.List; /** * * MyBatis Mapper CRUD * * @author Erwin Feng * @since 2021-04-01 */ @Component @Slf4j public class CategoryMapperTests extends MybatisPlusExampleApplicationTests { private CategoryEntity entity; @Resource private ICategoryMapper categoryMapper; // 创建数据 private void create() { entity = new CategoryEntity().setName("category test data"); categoryMapper.insert(entity); } // 清除数据 private void clear() { categoryMapper.deleteById(entity.getId()); } @Test public void testAdd() { entity = new CategoryEntity().setName("category test data"); int resultNum = categoryMapper.insert(entity); Assert.isTrue(resultNum == 1, "data add test failure"); clear(); } @Test public void testDelete() { create(); int resultNum = categoryMapper.deleteById(entity.getId()); Assert.isTrue(resultNum == 1, "data delete test failure"); } @Test public void testUpdate() { create(); entity.setName("category update"); int resultNum = categoryMapper.updateById(entity); Assert.isTrue(resultNum == 1, "data update test failure"); clear(); } @Test public void testQuery() { create(); List categoryEntityList = categoryMapper.selectList(new QueryWrapper<>()); for (CategoryEntity entity : categoryEntityList) { log.info(entity.toString()); } clear(); } } ================================================ FILE: src/test/java/com/fengwenyi/mybatisplusexample/CategoryModelTests.java ================================================ package com.fengwenyi.mybatisplusexample; import com.fengwenyi.mybatisplusexample.entity.CategoryEntity; import org.junit.jupiter.api.Test; import org.springframework.stereotype.Component; import org.springframework.util.Assert; /** * * model test * *

* CRUD *

* * @author Erwin Feng * @since 2021-03-30 */ @Component public class CategoryModelTests extends MybatisPlusExampleApplicationTests { private CategoryEntity model; // 创建数据 private void create() { model = new CategoryEntity().setName("category test data"); model.insert(); } // 清除数据 private void clear() { model.deleteById(); } @Test public void testAdd() { model = new CategoryEntity().setName("category test data"); boolean testResult = model.insert(); Assert.isTrue(testResult, "data add test failure"); clear(); } @Test public void testDelete() { create(); boolean testResult = model.deleteById(); Assert.isTrue(testResult, "data delete test failure"); } @Test public void testUpdate() { create(); model.setName("category update"); boolean testResult = model.updateById(); Assert.isTrue(testResult, "data update test failure"); clear(); } } ================================================ FILE: src/test/java/com/fengwenyi/mybatisplusexample/CategoryRepositoryTests.java ================================================ package com.fengwenyi.mybatisplusexample; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.fengwenyi.mybatisplusexample.entity.CategoryEntity; import com.fengwenyi.mybatisplusexample.repository.MPCategoryRepository; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.util.Assert; import java.util.List; /** * * MPCategoryRepository test * *

* CRUD *

* * @author Erwin Feng * @since 2021-03-08 */ @Component @Slf4j public class CategoryRepositoryTests extends MybatisPlusExampleApplicationTests { @Autowired private MPCategoryRepository mpCategoryRepository; private CategoryEntity entity; // 创建数据 private void create() { entity = new CategoryEntity().setName("category test data"); mpCategoryRepository.save(entity); } // 清除数据 private void clear() { mpCategoryRepository.removeById(entity); } @Test public void testAdd() { entity = new CategoryEntity().setName("category test data"); boolean testResult = mpCategoryRepository.save(entity); Assert.isTrue(testResult, "data add test failure"); clear(); } @Test public void testDelete() { create(); boolean testResult = mpCategoryRepository.removeById(entity.getId()); Assert.isTrue(testResult, "data delete test failure"); } @Test public void testDeleteByWrapper() { create(); LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper() .eq(CategoryEntity::getName, entity.getName()) ; boolean testResult = mpCategoryRepository.remove(queryWrapper); Assert.isTrue(testResult, "data delete by wrapper test failure"); } @Test public void testUpdate() { create(); entity.setName("category update"); boolean testResult = mpCategoryRepository.updateById(entity); Assert.isTrue(testResult, "data update test failure"); clear(); } @Test public void testQuery() { create(); // 根据主键ID查询 mpCategoryRepository.getById(entity.getId()); // 查询所有 List list = mpCategoryRepository.list(); // 添加查询 LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper() .eq(CategoryEntity::getName, entity.getName()) ; mpCategoryRepository.list(queryWrapper); // 分页查询 int currentPage = 1; int pageSize = 10; Page categoryPage = mpCategoryRepository.page(new Page<>(currentPage, pageSize), queryWrapper); long current = categoryPage.getCurrent(); long size = categoryPage.getSize(); long pages = categoryPage.getPages(); List categoryList = categoryPage.getRecords(); long total = categoryPage.getTotal(); clear(); } } ================================================ FILE: src/test/java/com/fengwenyi/mybatisplusexample/GoodsRepositoryTests.java ================================================ package com.fengwenyi.mybatisplusexample; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.fengwenyi.mybatisplusexample.entity.CategoryEntity; import com.fengwenyi.mybatisplusexample.entity.GoodsEntity; import com.fengwenyi.mybatisplusexample.entity.enums.GoodsFlagEnum; import com.fengwenyi.mybatisplusexample.repository.MPCategoryRepository; import com.fengwenyi.mybatisplusexample.repository.MPGoodsRepository; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.util.Assert; import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.List; /** * @author Erwin Feng * @since 2021-03-08 */ @Component @Slf4j public class GoodsRepositoryTests extends MybatisPlusExampleApplicationTests { @Autowired private MPCategoryRepository mpCategoryRepository; @Autowired private MPGoodsRepository mpGoodsRepository; @Test public void testAdd() { LambdaQueryWrapper categoryQueryWrapper = new LambdaQueryWrapper() .eq(CategoryEntity::getName, "家电") ; List categoryEntityList = mpCategoryRepository.list(categoryQueryWrapper); CategoryEntity categoryEntity = categoryEntityList.get(0); GoodsEntity goodsEntity = new GoodsEntity() .setName("平板电脑") .setPrice(new BigDecimal("4699.00")) .setStockNum(10000L) .setFlag(GoodsFlagEnum.UP) .setCategoryId(categoryEntity.getId()) .setCreateBy("admin") ; mpGoodsRepository.save(goodsEntity); } @Test public void testQuery() { List goodsEntityList = mpGoodsRepository.list(); for (GoodsEntity goodsEntity : goodsEntityList) { log.info(goodsEntity.toString()); log.info(goodsEntity.getFlag().getValue() + ""); } } @Test public void testUpdate() { LambdaQueryWrapper goodsQueryWrapper = new LambdaQueryWrapper().eq(GoodsEntity::getName, "平板电脑"); List goodsEntityList = mpGoodsRepository.list(goodsQueryWrapper); GoodsEntity goodsEntity = goodsEntityList.get(0); goodsEntity.setPrice(new BigDecimal("6499.00")); mpGoodsRepository.updateById(goodsEntity); } @Test void testQueryTime() { LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper() .lt(GoodsEntity::getCreateTime, LocalDateTime.parse("2021-04-08 10:00:00")) // where create_time < 2021-04-08 10:00:00 .gt(GoodsEntity::getCreateTime, LocalDateTime.parse("2021-04-09 10:00:00")) // where create_time > 2021-04-09 10:00:00 ; List list = mpGoodsRepository.list(queryWrapper); } } ================================================ FILE: src/test/java/com/fengwenyi/mybatisplusexample/MPWTests.java ================================================ package com.fengwenyi.mybatisplusexample; import com.baomidou.mybatisplus.core.toolkit.AES; import com.fengwenyi.javalib.util.PrintUtils; import org.junit.jupiter.api.Test; /** * @author Erwin Feng * @since 2021-03-31 */ public class MPWTests { @Test public void test() { // 生成 16 位随机 AES 密钥 String randomKey = AES.generateRandomKey(); PrintUtils.info(randomKey); String prefix = "mpw:"; // 随机密钥加密 String url = "jdbc:p6spy:mysql://192.168.16.128:3306/mybatis-plus-example"; String result =prefix + AES.encrypt(url, randomKey); PrintUtils.info(result); String username = "root"; result =prefix + AES.encrypt(username, randomKey); PrintUtils.info(result); String password = "123456"; result =prefix + AES.encrypt(password, randomKey); PrintUtils.info(result); /* 2021-03-31 00:57:49.494 INFO com.fengwenyi.mybatisplusexample.MPWTests#test : ba8a2cea8df4929f 2021-03-31 00:57:49.871 INFO com.fengwenyi.mybatisplusexample.MPWTests#test : mpw:pBZqJb+r/StFGyRopKJyhZN8dvstxaXxubewECBMWM3e7XK+719AUXlC1y1Cu14i1MwqEdiJbBa+sEwFzyWEgA== 2021-03-31 00:57:49.871 INFO com.fengwenyi.mybatisplusexample.MPWTests#test : mpw:hjR022j+cjKFE+35FLTQgg== 2021-03-31 00:57:49.871 INFO com.fengwenyi.mybatisplusexample.MPWTests#test : mpw:kzLGIBmi4hT+dPmqnXLAIw== */ } } ================================================ FILE: src/test/java/com/fengwenyi/mybatisplusexample/MybatisPlusExampleApplicationTests.java ================================================ package com.fengwenyi.mybatisplusexample; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest public class MybatisPlusExampleApplicationTests { @Test public void contextLoads() { } }