一、简介
Mybatis-Plus是一款 MyBatis 动态 sql 自动注入 crud 简化 增 删 改 查 操作中间件。启动加载 XML 配置时注入 mybatis 单表 动态 SQL 操作 ,为简化开发工作、提高生产率而生。Mybatis-Plus 启动注入非拦截实现、性能更优。
1.1、原理
1.2、特性
1.3、简化
MP简化了MyBatis的单表基本操作,提供了两种操作方式:
(1)、传统模式
Mybatis-Plus 通过 EntityWrapper(简称 EW,MP 封装的一个查询条件构造器)或者 Condition(与EW类似) 来让用户自由的构建查询条件,简单便捷,没有额外的负担,能够有效提高开发效率。
(2)、ActiveRecord模式
Active Record(简称AR)模式是软件里的一种架构性模式,主要概念是关系型数据库中的数据在内存中以对象的形式存储。由Martin Fowler在其2003年初版的书籍《Patterns of Enterprise Application Architecture》命名。遵循该模式的对象接口一般包括如Insert, Update, 和 Delete这样的函数,以及对应于底层数据库表字段的相关属性。
AR模式是一种访问数据库数据的方式。数据表或视图被映射成一个类。每个对象实例则对应于表的一条记录。对象被创建后,通过save就可以向表中新添一行记录。当对象被更新时,表中相应记录也被更新。这个包裹类通过属性或方法的形式实现访问表或视图中的每一个字段。
该模式主要被对象持久化工具采用,用于对象关系映射 (ORM). 典型的,外键关系会以合适的对象实例属性的形式暴露访问。
1.4、常用实体注解
1.表名注解@TableName
2.主键注解@TableId
3.字段注解@TableFieId
4.序列主键策略注解@KeySequence
二、搭建
环境:
IDEA
Spring Boot-2.0
MyBatis-Plus-2.2.0
MyBatisPlus-spring-boot-starter-1.0.5
druid-spring-boot-starter-1.1.9
首先创建SpringBoot Maven项目,然后加入以下依赖:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.fendo.mybatis.plus</groupId> <artifactId>demo</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>mybatis-plus</name> <description>mybatis-plus 示例</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.0.RELEASE</version> <relativePath/> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <mybatisplus-spring-boot-starter.version>1.0.5</mybatisplus-spring-boot-starter.version> <mybatisplus.version>2.2.0</mybatisplus.version> <fastjson.version>1.2.39</fastjson.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jetty</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- mybatis-plus begin --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatisplus-spring-boot-starter</artifactId> <version>${mybatisplus-spring-boot-starter.version}</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus</artifactId> <version>${mybatisplus.version}</version> </dependency> <!-- mybatis-plus end --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>${fastjson.version}</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.9</version> </dependency> <dependency> <groupId>com.jayway.restassured</groupId> <artifactId>rest-assured</artifactId> <version>2.9.0</version> </dependency> <!--Swagger UI--> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.2.2</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.2.2</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
然后新建application.yml配置文件
# Tomcat server: tomcat: uri-encoding: UTF-8 max-threads: 1000 min-spare-threads: 30 port: 8080 connection-timeout: 5000 #datasource spring: datasource: name: fendo url: jdbc:mysql://localhost:3306/fendo_plus_boot?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC&useSSL=false username: root password: root type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.jdbc.Driver initialSize: 5 minIdle: 5 maxActive: 20 maxWait: 60000 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 validationQuery: SELECT 1 FROM DUAL testWhileIdle: true testOnBorrow: false testOnReturn: false poolPreparedStatements: true maxPoolPreparedStatementPerConnectionSize: 20 spring.datasource.filters: stat,wall,log4j connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 # jackson时间格式化 jackson: time-zone: GMT+8 date-format: yyyy-MM-dd HH:mm:ss thymeleaf: cache: false prefix: classpath:/templates/ suffix: .html mode: LEGACYHTML5 encoding: UTF-8 check-template: false enabled: false resources: # 指定静态资源的路径 static-locations: classpath:/static/,classpath:/views/ mvc: view: prefix: /WEB-INF/ suffix: .jsp # Mybatis-Plus 配置 mybatis-plus: mapper-locations: classpath:/mapper/*Mapper.xml typeAliasesPackage: com.fendo.mybatis.plus.entity typeEnumsPackage: com.fendo.mybatis.plus.entity.enums # global-config: # id-type: 2 # field-strategy: 2 # db-column-underline: true # refresh-mapper: true # #capital-mode: true # #key-generator: com.baomidou.springboot.xxx # logic-delete-value: 0 # logic-not-delete-value: 1 # sql-injector: com.baomidou.mybatisplus.mapper.LogicSqlInjector # #meta-object-handler: com.baomidou.springboot.xxx # #sql-injector: com.baomidou.springboot.xxx # configuration: # map-underscore-to-camel-case: true # cache-enabled: false global-config: id-type: 3 #0:数据库ID自增 1:用户输入id 2:全局唯一id(IdWorker) 3:全局唯一ID(uuid) db-column-underline: false refresh-mapper: true configuration: map-underscore-to-camel-case: true cache-enabled: true #配置的缓存的全局开关 lazyLoadingEnabled: true #延时加载的开关 multipleResultSetsEnabled: true #开启的话,延时加载一个属性时会加载该对象全部属性,否则按需加载属性 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #打印sql语句,调试用
创建MyBatis-Plus配置类MybatisPlusConfig
/** * projectName: fendo-plus-boot * fileName: MybatisPlusConfig.java * packageName: com.fendo.mybatis.plus.config * date: 2018-01-12 23:13 * copyright(c) 2017-2020 xxx公司 */ package com.fendo.mybatis.plus.config; import com.baomidou.mybatisplus.plugins.PaginationInterceptor; import com.baomidou.mybatisplus.plugins.PerformanceInterceptor; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @version: V1.0 * @author: fendo * @className: MybatisPlusConfig * @packageName: com.fendo.mybatis.plus.config * @description: Mybatis-plus配置类 * @data: 2018-01-12 23:13 **/ @Configuration @MapperScan("com.fendo.mybatis.plus.mapper*") public class MybatisPlusConfig { /** * mybatis-plus SQL执行效率插件【生产环境可以关闭】 */ @Bean public PerformanceInterceptor performanceInterceptor() { return new PerformanceInterceptor(); } /** * 分页插件 */ @Bean public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); } }
三、测试
首先创建User表
CREATE TABLE `user` ( `id` varchar(32) NOT NULL, `name` varchar(255) DEFAULT NULL, `age` int(2) DEFAULT NULL, `sex` int(2) DEFAULT NULL, `create_by` varchar(255) DEFAULT NULL, `create_date` datetime DEFAULT NULL, `update_by` varchar(255) DEFAULT NULL, `update_date` datetime DEFAULT NULL, `remarks` varchar(255) DEFAULT NULL, `del_flag` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
然后创建controller,entity,service,mapper,项目结构如下所示:
Controller测试类如下:
/** * projectName: mybatis-plus * fileName: UserController.java * packageName: com.fendo.mybatis.plus.controller * date: 2018-03-24 19:07 * copyright(c) 2017-2020 xxx公司 */ package com.fendo.mybatis.plus.controller; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.mapper.EntityWrapper; import com.baomidou.mybatisplus.plugins.Page; import com.baomidou.mybatisplus.plugins.pagination.PageHelper; import com.fendo.mybatis.plus.common.utils.IdGen; import com.fendo.mybatis.plus.entity.UserEntity; import com.fendo.mybatis.plus.entity.enums.AgeEnum; import com.fendo.mybatis.plus.entity.enums.SexEnum; import com.fendo.mybatis.plus.service.UserService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @version: V1.0 * @author: fendo * @className: UserController * @packageName: com.fendo.mybatis.plus.controller * @description: 用户Controller * @data: 2018-03-24 19:07 **/ @RestController @RequestMapping("/user") @Api("用户操作接口") public class UserController { @Autowired private UserService userService; /** * 分页 PAGE */ @GetMapping("/page") @ApiOperation(value = "用户分页数据", response = UserEntity.class) @ApiResponse(code = 200, message = "success") public Page<UserEntity> test() { return userService.selectPage(new Page<UserEntity>(0,12)); } /** * AR 部分测试 */ @GetMapping("/insert") public Page<UserEntity> insert() { UserEntity user = new UserEntity(IdGen.getUUID(), "testAr", AgeEnum.ONE, SexEnum.FEMALE); System.err.println("删除所有:" + user.delete(null)); user.insert(); System.err.println("查询插入结果:" + user.selectById().toString()); user.setName("mybatis-plus-ar"); System.err.println("更新:" + user.updateById()); return user.selectPage(new Page<UserEntity>(0, 12), null); } /** * 增删改查 CRUD */ @GetMapping("/crud") public UserEntity crud() { System.err.println("删除一条数据:" + userService.deleteById("85349feb19f04fa78a7a717f4dce031f")); System.err.println("deleteAll:" + userService.deleteAll()); String IdGens = IdGen.getUUID(); System.err.println("插入一条数据:" + userService.insert(new UserEntity(IdGens, "张三", AgeEnum.TWO, SexEnum.FEMALE))); UserEntity user = new UserEntity("张三", AgeEnum.TWO, SexEnum.MALE); boolean result = userService.insert(user); // 自动回写的ID String id = user.getId(); System.err.println("插入一条数据:" + result + ", 插入信息:" + user.toString()); System.err.println("查询:" + userService.selectById(id).toString()); System.err.println("更新一条数据:" + userService.updateById(new UserEntity(IdGens, "三毛", AgeEnum.ONE, SexEnum.FEMALE))); for (int i = 0; i < 5; ++i) { userService.insert(new UserEntity( IdGen.getUUID(), "张三" + i, AgeEnum.ONE, SexEnum.FEMALE)); } Page<UserEntity> userListPage = userService.selectPage(new Page<UserEntity>(1, 5), new EntityWrapper<>(new UserEntity())); System.err.println("total=" + userListPage.getTotal() + ", current list size=" + userListPage.getRecords().size()); return userService.selectById(IdGens); } /** * 插入 OR 修改 */ @GetMapping("/save") public UserEntity save() { String IdGens = IdGen.getUUID(); UserEntity user = new UserEntity(IdGens, "王五", AgeEnum.ONE, SexEnum.FEMALE); userService.insertOrUpdate(user); return userService.selectById(IdGens); } @GetMapping("/add") public Object addUser() { UserEntity user = new UserEntity(IdGen.getUUID(), "张三'特殊`符号", AgeEnum.TWO, SexEnum.FEMALE); JSONObject result = new JSONObject(); result.put("result", userService.insert(user)); return result; } @GetMapping("/selectsql") public Object getUserBySql() { JSONObject result = new JSONObject(); result.put("records", userService.selectListBySQL()); return result; } /** * 7、分页 size 一页显示数量 current 当前页码 * 方式一:http://localhost:8080/user/page?size=1¤t=1 * 方式二:http://localhost:8080/user/pagehelper?size=1¤t=1 */ // 参数模式分页 @GetMapping("/pages") public Object page(Page page) { return userService.selectPage(page); } // ThreadLocal 模式分页 @GetMapping("/pagehelper") public Object pagehelper(Page page) { PageHelper.setPagination(page); page.setRecords(userService.selectList(null)); page.setTotal(PageHelper.freeTotal());//获取总数并释放资源 也可以 PageHelper.getTotal() return page; } /** * 测试事物 * http://localhost:8080/user/test_transactional<br> * 访问如下并未发现插入数据说明事物可靠!!<br> * http://localhost:8080/user/test<br> * <br> * 启动 Application 加上 @EnableTransactionManagement 注解其实可无默认貌似就开启了<br> * 需要事物的方法加上 @Transactional 必须的哦!! */ @Transactional @GetMapping("/test_transactional") public void testTransactional() { String IdGens = IdGen.getUUID(); userService.insert(new UserEntity(IdGens, "测试事物", AgeEnum.ONE, SexEnum.FEMALE)); System.out.println(" 这里手动抛出异常,自动回滚数据 : " + IdGens); throw new RuntimeException(); } }
四、代码生成
创建测试类GeneratorStart
/** * projectName: fendo-plus-boot * fileName: GeneratorStart.java * packageName: com.fendo.shiro.generator * date: 2018-01-15 19:59 * copyright(c) 2017-2020 xxx公司 */ package com.gzsys.modules.gen; import com.baomidou.mybatisplus.generator.AutoGenerator; import com.baomidou.mybatisplus.generator.InjectionConfig; import com.baomidou.mybatisplus.generator.config.*; import com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder; import com.baomidou.mybatisplus.generator.config.po.TableInfo; import com.baomidou.mybatisplus.generator.config.rules.DbType; import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @version: V1.0 * @author: fendo * @className: GeneratorStart * @packageName: com.gzsys.modules.gen * @description: 代码生成器 * @data: 2018-01-15 19:59 **/ public class GeneratorStart { public static void main(String[] args) { String packageName = "com.gzsys.modules.yun"; boolean serviceNameStartWithI = false;//user -> UserService, 设置成true: user -> IUserService generateByTables(serviceNameStartWithI, packageName, "USER_LOGIN"); } private static void generateByTables(boolean serviceNameStartWithI, String packageName, String... tableNames) { // 全局配置 GlobalConfig config = new GlobalConfig(); AutoGenerator mpg = new AutoGenerator(); String dbUrl = "jdbc:oracle:thin:@//106.14.160.67:1521/test"; DataSourceConfig dataSourceConfig = new DataSourceConfig(); dataSourceConfig.setDbType(DbType.ORACLE) .setUrl(dbUrl) .setUsername("test") .setPassword("Eru43wPo") .setDriverName("oracle.jdbc.driver.OracleDriver"); // 策略配置 StrategyConfig strategyConfig = new StrategyConfig(); strategyConfig .setCapitalMode(true) // 全局大写命名 ORACLE 注意 .setEntityLombokModel(false) //实体 是否为lombok模型(默认 false) .setDbColumnUnderline(true) //表名、字段名、是否使用下划线命名 .setNaming(NamingStrategy.underline_to_camel) //表名生成策略 .setInclude(tableNames);//修改替换成你需要的表名,多个表名传数组 // strategyConfig.setExclude(new String[]{"test"}); // 排除生成的表 // 自定义实体父类 strategyConfig.setSuperEntityClass("com.gzsys.common.persistence.BaseEntity"); // 自定义实体,公共字段 strategyConfig.setSuperEntityColumns(new String[] { "ID", "CREATE_TIME", "CREATE_NAME" , "UPDATE_TIME", "UPDATE_NAME", "STATE"}); // 自定义 mapper 父类 // strategyConfig.setSuperMapperClass("com.baomidou.demo.TestMapper"); // 自定义 service 父类 //strategyConfig.setSuperServiceClass("com.baomidou.demo.TestService"); // 自定义 service 实现类父类 //strategyConfig.setSuperServiceImplClass("com.baomidou.demo.TestServiceImpl"); // 自定义 controller 父类 strategyConfig.setSuperControllerClass("com.gzsys.common.base.controller.BaseController"); // 【实体】是否生成字段常量(默认 false) // public static final String ID = "test_id"; // strategyConfig.setEntityColumnConstant(true); // 【实体】是否为构建者模型(默认 false) // public User setName(String name) {this.name = name; return this;} // strategyConfig.setEntityBuliderModel(true); config.setActiveRecord(true) //是否 开启 ActiveRecord 模式 .setAuthor("fendo") .setOutputDir("d:\\codeGen") .setFileOverride(true) .setActiveRecord(true) .setEnableCache(false)// XML 二级缓存 .setBaseResultMap(true)// XML ResultMap .setBaseColumnList(false);// XML columList if (!serviceNameStartWithI) { config.setServiceName("%sService"); //自定义Service后戳,注意 %s 会自动填充表实体属性! } // 注入自定义配置,可以在 VM 中使用 cfg.abc 【可无】 InjectionConfig cfg = new InjectionConfig() { @Override public void initMap() { Map<String, Object> map = new HashMap<String, Object>(); map.put("abc", this.getConfig().getGlobalConfig().getAuthor() + "-mp"); this.setMap(map); } }; // 自定义 xxList.jsp 生成 List<FileOutConfig> focList = new ArrayList<FileOutConfig>(); //focList.add(new FileOutConfig("/template/list.jsp.vm") { // @Override // public String outputFile(TableInfo tableInfo) { // // 自定义输入文件名称 // return "D://my_" + tableInfo.getEntityName() + ".jsp"; // } //}); //cfg.setFileOutConfigList(focList); //mpg.setCfg(cfg); // 调整 xml 生成目录演示 focList.add(new FileOutConfig("/templates/mapper.xml.vm") { @Override public String outputFile(TableInfo tableInfo) { return "d:\\codeGen/resources/mapping/modules/yun/" + tableInfo.getEntityName() + ".xml"; } }); cfg.setFileOutConfigList(focList); mpg.setCfg(cfg); // 关闭默认 xml 生成,调整生成 至 根目录 TemplateConfig tc = new TemplateConfig(); tc.setXml(null); mpg.setTemplate(tc); // 自定义模板配置,可以 copy 源码 mybatis-plus/src/main/resources/templates 下面内容修改, // 放置自己项目的 src/main/resources/templates 目录下, 默认名称一下可以不配置,也可以自定义模板名称 // TemplateConfig tc = new TemplateConfig(); // tc.setController("..."); // tc.setEntity("..."); // tc.setMapper("..."); // tc.setXml("..."); // tc.setService("..."); // tc.setServiceImpl("..."); // 如上任何一个模块如果设置 空 OR Null 将不生成该模块。 // mpg.setTemplate(tc); //生成文件 配置 mpg.setGlobalConfig(config) //全局 相关配置 .setDataSource(dataSourceConfig) //数据源配置 .setStrategy(strategyConfig) //数据库表配置 .setPackageInfo( //包 相关配置 new PackageConfig() //跟包相关的配置项 .setParent(packageName) //父包名。如果为空,将下面子包名必须写全部, 否则就只需写子包名 .setController("controller") //Controller包名 .setEntity("entity") //entity包名 //.setXml("/") ) .execute(); } private void generateByTables(String packageName, String... tableNames) { generateByTables(true, packageName, tableNames); } }
Spring Boot 整合mybatis 与 swagger2
到此这篇关于MyBatis-Plus集成Druid环境搭建的详细教程的文章就介绍到这了,更多相关MyBatis-Plus环境搭建内容请搜索小牛知识库以前的文章或继续浏览下面的相关文章希望大家以后多多支持小牛知识库!
本文向大家介绍Python3开发环境搭建详细教程,包括了Python3开发环境搭建详细教程的使用技巧和注意事项,需要的朋友参考一下 Python 环境安装 下载 Python 安装包 进入 python 官网 ,在Downloads(下载)下面,点击 Window 进入下载列表页 这里我们看到两个大类: Stable Releases 稳定版本:经过测试和使用迭代,bug较少。可用于工作学习 Pr
本文向大家介绍Vue环境搭建+VSCode+Win10的详细教程,包括了Vue环境搭建+VSCode+Win10的详细教程的使用技巧和注意事项,需要的朋友参考一下 一、安装Node.js(js的运行环境) 1.在Node.js官网https://nodejs.org/en/download/ 下载安装包。 2.下载后进行安装。 3.打开命令行,输入node -v可以查看到版本号。输入npm –v可
本文向大家介绍MacOS下本地golang环境搭建详细教程,包括了MacOS下本地golang环境搭建详细教程的使用技巧和注意事项,需要的朋友参考一下 安装golang 使用homebrew安装golang。homebrew是MacOS 平台下的软件包管理工具,拥有安装、卸载、更新、查看、搜索等功能。开发者不需要关心依赖和文件路径。如果系统没有安装homebrew,终端内执行以下命令安装homeb
本文向大家介绍CentOS 7.x下的LEMP环境搭建详细教程,包括了CentOS 7.x下的LEMP环境搭建详细教程的使用技巧和注意事项,需要的朋友参考一下 最近由于项目需求,将服务器从CentOS6升级到CentOS7,对应的PHP版本也升级到PHP5.6。我们熟悉的有LEMP环境一键安装包,但是本文我们将单独安装各个组件模块,并搭建一个完整的PHP运行平台。 我们常说的LNMP环境是指Lin
本文向大家介绍PHP环境搭建的详细步骤,包括了PHP环境搭建的详细步骤的使用技巧和注意事项,需要的朋友参考一下 接着上篇继续学习,谈谈IIS支持php怎么配置: 1、去php官网下载php:http://windows.php.net/download/ 。IIS7我们需要选择php 5.3 以上的VC9包,下载zip文件。如图: 2、将 zip 包解压缩到自己选择的目录,例如 C:\PHP\。解
本文向大家介绍MyBatis Plus 入门使用详细教程,包括了MyBatis Plus 入门使用详细教程的使用技巧和注意事项,需要的朋友参考一下 一、MyBatis Plus 介绍 MyBatis Plus 是国内人员开发的 MyBatis 增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。 MyBatis Plus 的核心功能有:支持通用的 CRUD、代码生成器