MybatisPlus最全教程

@[TOC](文章目录) # 1、介绍 官网:[https://baomidou.com/](https://baomidou.com/) 简介:`MyBatis-Plus (opens new window)(简称 MP)`是一个 MyBatis (opens new window)的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。 >愿景: 我们的愿景是成为 MyBatis 最好的搭档,就像 魂斗罗 中的 1P、2P,基友搭配,效率翻倍。 ![在这里插入图片描述](https://www.icode9.com/i/ll/?i=20210608133105133.png?,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3pob3VoZW5nemhl,size_16,color_FFFFFF,t_70) 特性: 1、无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑 2、损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作 3、强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求 4、支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错 5、支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可*配 置,完美解决主键问题 6、支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作 7、支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere ) 8、内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层 代码,支持模板引擎,更有超多自定义配置等您来使用 9、内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于 普通 List 查询 10、分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、 Postgre、SQLServer 等多种数据库 11、内置性能分析插件:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查 询 12、内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操 作 架构设计: ![在这里插入图片描述](https://www.icode9.com/i/ll/?i=20210608133930789.jpg?,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3pob3VoZW5nemhl,size_16,color_FFFFFF,t_70) # 2、基于mybatis-plus的入门 ## 2.1、mybatis与mybatis-plus实现方式对比 >基于 Mybatis 需要编写 xxxMapper 接口,并手动编写 CRUD 方法 提供 xxxMapper.xml 映射文件,并手动编写每个方法对应的 SQL 语句. 基于 Mybatis-plus 只需要创建 xxxMapper 接口, 并继承BaseMapper 接口.这就是使用 mybatis-plus 需要完成的所有操作,甚至不需要创建 SQL 映射文件 ## 2.2、BaseMapper接口介绍 ### 2.2.1、如何理解核心接口BaseMapper? >在使用Mybatis-Plus是,核心操作类是BaseMapper接口,其最终也是利用的Mybatis接口编程的实现机制,其默认提供了一系列的增删改查的基础方法,并且开发人员对于这些基础操作不需要写SQL进行处理操作(Mybatis提供的机制就是需要开发人员在mapper.xml中提供sql语句),那样我们可以猜测肯定是Mybatis-Plus完成了BaseMapper接口提供的方法的SQL语句的生成操作。 ### 2.2.2、BaseMapper接口为我们定义了哪些方法? ![在这里插入图片描述](https://www.icode9.com/i/ll/?i=20210608170644704.png?,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3pob3VoZW5nemhl,size_16,color_FFFFFF,t_70) ### 2.2.3、BaseMapper接口源码 ```java package com.baomidou.mybatisplus.core.mapper; import java.io.Serializable; import java.util.Collection; import java.util.List; import java.util.Map; import org.apache.ibatis.annotations.Param; import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.Constants; public interface BaseMapper { /** *

* 插入一条记录 *

* * @param entity 实体对象 */ int insert(T entity); /** *

* 根据 ID 删除 *

* * @param id 主键ID */ int deleteById(Serializable id); /** *

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

* * @param columnMap 表字段 map 对象 */ int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap); /** *

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

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

* 删除(根据ID 批量删除) *

* * @param idList 主键ID列表(不能为 null 以及 empty) */ int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList); /** *

* 根据 ID 修改 *

* * @param entity 实体对象 */ int updateById(@Param(Constants.ENTITY) T entity); /** *

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

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

* 根据 ID 查询 *

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

* 查询(根据ID 批量查询) *

* * @param idList 主键ID列表(不能为 null 以及 empty) */ List selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList); /** *

* 查询(根据 columnMap 条件) *

* * @param columnMap 表字段 map 对象 */ List selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap); /** *

* 根据 entity 条件,查询一条记录 *

* * @param queryWrapper 实体对象 */ T selectOne(@Param(Constants.WRAPPER) Wrapper queryWrapper); /** *

* 根据 Wrapper 条件,查询总记录数 *

* * @param queryWrapper 实体对象 */ Integer selectCount(@Param(Constants.WRAPPER) Wrapper queryWrapper); /** *

* 根据 entity 条件,查询全部记录 *

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

* 根据 Wrapper 条件,查询全部记录 *

* * @param queryWrapper 实体对象封装操作类(可以为 null) */ List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper queryWrapper); /** *

* 根据 Wrapper 条件,查询全部记录 * 注意: 只返回第一个字段的值 *

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

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

* * @param page 分页查询条件(可以为 RowBounds.DEFAULT) * @param queryWrapper 实体对象封装操作类(可以为 null) */ IPage selectPage(IPage page, @Param(Constants.WRAPPER) Wrapper queryWrapper); /** *

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

* * @param page 分页查询条件 * @param queryWrapper 实体对象封装操作类 */ IPage<Map<String, Object>> selectMapsPage(IPage page, @Param(Constants.WRAPPER) Wrapper queryWrapper); } ``` ### 2.2.4、mybatis-plus中常用的注解 ![在这里插入图片描述](https://www.icode9.com/i/ll/?i=20210608171923538.png?,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3pob3VoZW5nemhl,size_16,color_FFFFFF,t_70) ```java @TableName:对数据表名注解 @TableId:表主键标识 @TableId(value = "id", type = IdType.AUTO):自增 @TableId(value = "id", type = IdType.ID_WORKER_STR):分布式全局唯一ID字符串类型 @TableId(value = "id", type = IdType.INPUT):自行输入 @TableId(value = "id", type = IdType.ID_WORKER):分布式全局唯一ID 长整型类型 @TableId(value = "id", type = IdType.UUID):32位UUID字符串 @TableId(value = "id", type = IdType.NONE):无状态 @TableField:表字段标识 @TableField(exist = false):表示该属性不为数据库表字段,但又是必须使用的。 @TableField(exist = true):表示该属性为数据库表字段。 @TableField(condition = SqlCondition.LIKE):表示该属性可以模糊搜索。 @TableField(fill = FieldFill.INSERT):注解填充字段 ,生成器策略部分也可以配置! @FieldStrategy: @FieldFill @Version:乐观锁注解、标记 @EnumValue:通枚举类注解 @TableLogic:表字段逻辑处理注解(逻辑删除) @SqlParser:租户注解 @KeySequence:序列主键策略 ``` 更多请看[https://baomidou.com/guide/annotation.html#tablefield](https://baomidou.com/guide/annotation.html#tablefield) # 3、快速使用 ## 3.1引入依赖 ```java com.baomidoumybatis‐plus3.3.1com.baomidoumybatis‐plus‐boot‐starter3.3.1org.projectlomboklombok1.18.12 ``` ==注意:不需要再引用mybatis与mybatis-spring的maven依赖== ## 3.2、创建数据库 ```sql CREATE TABLE `t_student` ( `sid` int(10) NOT NULL AUTO_INCREMENT, `s_name` varchar(100) NOT NULL, `sage` int(3) DEFAULT NULL, `ssex` char(1) DEFAULT NULL, `sphone` char(11) DEFAULT NULL, PRIMARY KEY (`sid`) ) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8; INSERT INTO `t_student` (`sid`, `s_name`, `sage`, `ssex`, `sphone`) VALUES ('4', '张三', '18', '1', '12345678912'); INSERT INTO `t_student` (`sid`, `s_name`, `sage`, `ssex`, `sphone`) VALUES ('5', '李四', '20', '1', '12467897452'); INSERT INTO `t_student` (`sid`, `s_name`, `sage`, `ssex`, `sphone`) VALUES ('8', '小丽', '15', '2', '4678'); INSERT INTO `t_student` (`sid`, `s_name`, `sage`, `ssex`, `sphone`) VALUES ('9', '赵六六', '15', '1', '7897564'); INSERT INTO `t_student` (`sid`, `s_name`, `sage`, `ssex`, `sphone`) VALUES ('10', '小特', '50', '1', '4564654'); ``` ![在这里插入图片描述](https://www.icode9.com/i/ll/?i=2021060814490977.png?,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3pob3VoZW5nemhl,size_16,color_FFFFFF,t_70) ## 3.3、配置application.yml ```yaml # 设置开发环境 spring: profiles: active: dev #数据库连接 datasource: url: jdbc:mysql://localhost:3307/mybatisplus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8 driver-class-name: com.mysql.cj.jdbc.Driver type: com.alibaba.druid.pool.DruidDataSource username: root password: 489773 # 配置日志 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 配置逻辑删除 global-config: db-config: logic-delete-value: 1 logic-not-delete-value: 0 ``` ## 3.4、创建pojo实体类 ```java package com.zhz.pojo; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.extension.activerecord.Model; import java.io.Serializable; import java.util.Objects; /** * @author zhz * @date 2020/03/24 **/ @Data @AllArgsConstructor @NoArgsConstructor @ToString @TableName(value = "t_student") public class Student{ /* * @TableId: * value: 指定表中的主键列的列名, 如果实体属性名与列名一致,可以省略不指定. * type: 指定主键策略. */ @TableId(type = IdType.AUTO) private Integer sid; @TableField("s_name") private String sname; private Integer sage; private String ssex; private String sphone; } ``` ## 3.5、增删查改操作 编写StudentMapper接口继承BaseMapper接口 ```java package com.zhz.mapper; import org.apache.ibatis.annotations.Mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.example.demo.pojo.Student; /** * @author zhz *基于Mybatis‐plus实现: 让XxxMapper接口继承 BaseMapper接口即可. *BaseMapper : 泛型指定的就是当前Mapper接口所操作的实体类类型 */ @Mapper public interface StudentMapperextends BaseMapper { } ``` 准备测试类(直接套在平常的开发上也是可以的) ```java package com.zhz.test; import java.util.Map; import java.util.ArrayList; import java.util.HashMap; import com.zhz.mapper.StudentMapper; import com.bjsxt.pojo.Student; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import java.util.List; /** * @author zhz * @date 2020/03/26 * mybatisPlus基本增删查改 **/ public class TestMybatisPlusBase { @Autowired private StudentMapper studentMapper; /** * 测试使用mp查询所有学生信息 */ @Test public void testSelAllStu() { //查询所有学生信息 List students = studentMapper.selectList(null); //输出结果 for (Student student:students) { System.out.println(student); } } /** * 测试使用Mp完成新增 */ @Test public void testIns() { //创建学生对象存储要新增的学生信息 Student student = new Student(); student.setSname("赵六六"); student.setSage(15); student.setSsex("1"); student.setSphone("7897564"); //新增学生信息 int insert = studentMapper.insert(student); //输出结果 System.out.println("添加得数量:"+insert); System.out.println("主键:"+student.getSid()); } /** * 测试使用Mp完成修改 */ @Test public void testUp(){ //创建学生对象存储要修改的学生信息 Student student = new Student(); student.setSid(6); student.setSage(20); //修改学生信息 int i = studentMapper.updateById(student); //输出结果 System.out.println("修改的条数:"+i); } /** * 删除:通过ID删除 */ @Test public void testDelById(){ //根据ID删除学生信息 int i = studentMapper.deleteById(7); //输出结果 System.out.println("删除的条数:"+i); } /** * 删除:指定条件删除数据(deleteByMap) */ @Test public void testDelByMap(){ Map<String,Object> map = new HashMap<>(); map.put("s_name","小红"); //指定条件删除学生信息 int i = studentMapper.deleteByMap(map); System.out.println("删除的条数:"+i); } /** * 删除:多选删除 */ @Test public void testDelByIds(){ List list = new ArrayList<>(); list.add(6); list.add(7); //删除符合Id要求的数据 int i = studentMapper.deleteBatchIds(list); System.out.println("删除的条数:"+i); } /** * 查询:通过ID查询 */ @Test public void testSelById(){ //根据ID查询学生信息 Student student = studentMapper.selectById(4); //输出结果 System.out.println(student); } /** * 查询:通过指定的条件完成查询 */ @Test public void testSelByMap(){ Map<String, Object> map = new HashMap<> (); map.put("s_name","张三"); //指定查询条件查询学生信息 List list = studentMapper.selectByMap(map); //输出结果 System.out.println(list); } /** * 查询:根据ID集合获取数据 */ @Test public void testSelectBatchIds(){ List list = new ArrayList<>(); list.add(4); list.add(5); //指定查询条件查询学生信息 List students = studentMapper.selectBatchIds(list); //输出结果 System.out.println(students); } } ``` # 4、条件构造器:Wrapper 结构: ![在这里插入图片描述](https://www.icode9.com/i/ll/?i=20210608202852955.png?,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3pob3VoZW5nemhl,size_16,color_FFFFFF,t_70) ## 4.1、7种构造器介绍 ### 4.1.1、Wrapper >条件构造抽象类,最顶端父类,抽象类中提供3个方法以及其他方法 ![在这里插入图片描述](https://www.icode9.com/i/ll/?i=20210608203149646.png?,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3pob3VoZW5nemhl,size_16,color_FFFFFF,t_70) ### 4.1.2、AbstractWrapper >用于查询条件封装,生成 sql 的 where 条件,QueryWrapper(LambdaQueryWrapper) 和UpdateWrapper(LambdaUpdateWrapper) 的父类用于生成 sql 的 where 条件, entity 属性也用于生成 sql 的 where条件 ![在这里插入图片描述](https://www.icode9.com/i/ll/?i=2021060820502613.png?,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3pob3VoZW5nemhl,size_16,color_FFFFFF,t_70) 重要的方法: ![在这里插入图片描述](https://www.icode9.com/i/ll/?i=20210608205126812.png?,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3pob3VoZW5nemhl,size_16,color_FFFFFF,t_70) ### 4.1.3、AbstractLambdaWrapper >Lambda 语法使用 Wrapper统一处理解析 lambda 获取 column。 ### 4.1.4、LambdaQueryWrapper >用于Lambda语法使用的查询Wrapper ### 4.1.5、LambdaUpdateWrapper >Lambda 更新封装Wrapper ### 4.1.6、QueryWrapper > Entity 对象封装操作类,不是用lambda语法,自身的内部属性 entity 也用于生成 where 条件 ![在这里插入图片描述](https://www.icode9.com/i/ll/?i=20210608213739966.png) ```java select(String... sqlSelect) select(Predicate predicate) select(Class entityClass, Predicate predicate) /* 例: select("id", "name", "age") 例: select(i ‐> i.getProperty().startsWith("zhz")) */ ``` ### 4.1.7、UpdateWrapper > Update 条件封装,用于Entity对象更新操作 ![在这里插入图片描述](https://www.icode9.com/i/ll/?i=20210608213854959.png?,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3pob3VoZW5nemhl,size_16,color_FFFFFF,t_70) set方法 ```java set(String column, Object val) set(boolean condition, String column, Object val) /* SQL SET 字段 例: set("name", "zhz") 例: set("name", "")‐‐‐>数据库字段值变为空字符串 例: set("name", null)‐‐‐>数据库字段值变为null 说明:boolean condition为控制该字段是否拼接到最终的sql语句中 */ ``` setSql方法 ```java setSql(String sql) /* 设置 SET 部分 SQL 例: setSql("name = '老李头'") */ ``` ## 4.2、带条件的CURD ### 4.2.1、带条件的查询 ```java // 根据 entity 条件,查询一条记录 T selectOne(@Param(Constants.WRAPPER) Wrapper queryWrapper); // 根据 entity 条件,查询全部记录 List selectList(@Param(Constants.WRAPPER) Wrapper queryWrapper); // 根据 Wrapper 条件,查询全部记录 List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper queryWrapper); // 根据 Wrapper 条件,查询全部记录。注意: 只返回第一个字段的值 List selectObjs(@Param(Constants.WRAPPER) Wrapper queryWrapper); // 根据 entity 条件,查询全部记录(并翻页) IPage selectPage(IPage page, @Param(Constants.WRAPPER) Wrapper queryWrapper); // 根据 Wrapper 条件,查询全部记录(并翻页) IPage<Map<String, Object>> selectMapsPage(IPage page, @Param(Constants.WRAPPER) Wrapper queryWrapper); // 根据 Wrapper 条件,查询总记录数 Integer selectCount(@Param(Constants.WRAPPER) Wrapper queryWrapper); ``` ### 4.2.2、带条件的更新 ```java @Test void update() { UpdateWrapper updateWrapper=new UpdateWrapper(); updateWrapper.eq("s_name", "张三").eq("sage", 18).set("id", 100); empolyeeMapper.update(student, updateWrapper); } ``` ### 4.2.3、带条件的删除 ```java // 根据 entity 条件,删除记录 int delete(@Param(Constants.WRAPPER) Wrapper wrapper); // 根据 columnMap 条件,删除记录 int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap); ``` ## 4.3、wrapper查询实例 ```java package com.zhz; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.zhz.mapper.StudentMapper; import com.zhz.pojo.Student; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import java.util.List; import java.util.Map; @SpringBootTest public class WrapperTest { @Autowired private StudentMapper studentMapper; @Test void contextLoads() { // 查询name不为空的用户,并且邮箱不为空的用户,年龄大于等于12 QueryWrapper wrapper = new QueryWrapper<>(); wrapper .isNotNull("s_name") .ge("sage",12); userMapper.selectList(wrapper).forEach(System.out::println); } @Test void test2(){ // 查询名字zhz QueryWrapper wrapper = new QueryWrapper<>(); wrapper.eq("s_name","zhz"); User user = userMapper.selectOne(wrapper); // 查询一个数据,出现多个结果使用List 或者 Map System.out.println(user); } @Test void test3(){ // 查询年龄在 20 ~ 30 岁之间的用户 QueryWrapper wrapper = new QueryWrapper<>(); wrapper.between("sage",20,30); // 区间 Integer count = userMapper.selectCount(wrapper);// 查询结果数 System.out.println(count); } // 模糊查询 @Test void test4(){ // 查询年龄在 20 ~ 30 岁之间的用户 QueryWrapper wrapper = new QueryWrapper<>(); // 左和右 t% wrapper .notLike("s_name","e") .likeRight("sphone","131"); List<Map<String, Object>> maps = userMapper.selectMaps(wrapper); maps.forEach(System.out::println); } // 模糊查询 @Test void test5(){ QueryWrapper wrapper = new QueryWrapper<>(); // id 在子查询中查出来 wrapper.inSql("sid","select sid from student where sid<3"); List objects = userMapper.selectObjs(wrapper); objects.forEach(System.out::println); } //测试六 @Test void test6(){ QueryWrapper wrapper = new QueryWrapper<>(); // 通过id进行排序 wrapper.orderByAsc("sid"); List users = userMapper.selectList(wrapper); users.forEach(System.out::println); } } ``` # 5、扩展 ## 5.1、全局ID生成策略 配置了之后就不需要在实体类主键上配置了 ```yaml mybatis-plus: global‐config: db‐config: id‐type: auto ``` ## 5.2、逻辑删除 **物理删除:** 在删除的时候直接将数据从数据库干掉DELTE **逻辑删除:** 从逻辑层面控制删除,通常会在表里添加一个逻辑删除的字段比如 enabled 、is_delete ,数据默认是有效的(值为1),当用户删除时将数据修改UPDATE 0, 在查询的时候就只查where enabled=1. 1. 需要添加逻辑删除的字段 2. 局部单表逻辑删除,需要在对应的pojo类加入对应的逻辑删除标识字段 ```java @TableLogic // 代表逻辑删除(单个字段的) private Integer flag; ``` 开启==全局逻辑删除==配置,如果进行了全局逻辑删除配置并且指定了,就可以不用在每个实体类中配置了@TableLogic ```yaml mybatis-plus: global-config: db-config: logic-not-delete-value: # 逻辑未删除值(默认为 0) logic-delete-value: 1 # 逻辑已删除值(默认为 1) logic‐delete‐field: flag # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置@TableLogic) ``` 数据库配置: ![在这里插入图片描述](https://www.icode9.com/i/ll/?i=20210608231248468.png?,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3pob3VoZW5nemhl,size_16,color_FFFFFF,t_70) 当执行删除, 将会把逻辑删除字段进行修改 ```java @Test void logicDel(){ studentService.removeById(1); } ``` 执行的sql语句为 ```sql update t_student set flag=0 where sid=? and flag=1 ``` 当执行查询时。会自动查询有效数据 where flag=1 ```java @Test void logicList(){ studentService.list(); } ``` sql为: ```sql select sid,s_name,sage,ssex,sphone,flag from s_student where flag=1 ``` ## 5.3、执行SQL分析打印(好东西) ### 5.3.1、依赖 ```java p6spyp6spy3.9.1 ``` ### 5.3.2、yml配置 ```yaml spring: datasource: url: jdbc:p6spy:mysql://localhost:3307/mybatisplus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8 driver-class-name: com.p6spy.engine.spy.P6SpyDriver ``` ### 5.3.3、添加p6spy:spy.properties ```yaml #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 ``` ### 5.3.4、SQL 日志美化插件: ![在这里插入图片描述](https://www.icode9.com/i/ll/?i=20210608233050323.png) ![在这里插入图片描述](https://www.icode9.com/i/ll/?i=20210608233054537.png) 地址: ### 5.3.5、数据安全保护 防止删库跑路 #### 5.3.5.1、得到16位随机秘钥 ```java @Test void test(){// 生成 16 位随机 AES 密钥 String randomKey = AES.generateRandomKey(); System.out.println(randomKey); } da12166c7db8a58f ``` #### 5.3.5.2、根据秘钥加密 数据库连接信息 ```java @Test void test(){ String url = AES.encrypt("jdbc:mysql://localhost:3307/mybatisplus?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&" , "da12166c7db8a58f"); String uname = AES.encrypt("root" , "da12166c7db8a58f"); String pwd = AES.encrypt("489773" , "da12166c7db8a58f"); System.out.println(url); System.out.println(uname); System.out.println(pwd); } ``` #### 5.3.5.3、修改配置文件 注意要mpw:开头(联系上面) ```yaml spring: datasource: url: mpw:上面得到的url的加密密码 username: mpw:上面得到的username的加密密码 password: mpw:上面得到的password的加密密码 driver-class-name: com.p6spy.engine.spy.P6SpyDriver ``` #### 5.3.5.4、在部署的时候需要解密 ```java java ‐jar xxxx.jar ‐‐mpw.key=你的16位随机秘钥, 越少人知道越好(5.3.5.1得到的随机码) ``` ## 5.4、乐观锁插件使用 ### 5.4.1、介绍 >`悲观锁:` >1. 悲观锁,正如其名,具有强烈的独占和排他特性。它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度。因此,在整个数据处理过程中,将数据处于锁定状态。 >2. 假设功能并发量非常大,就需要使用 synchronized 来处理高并发下产生线程不安全问题, 会使其他线程进行挂起等待从而影响系统吞吐量 >`乐观锁:` >1. 乐观锁是相对悲观锁而言的,乐观锁假设数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则返回给用户错误的信息,让用户决定如何去做。乐观锁适用于`读多写少`的场景,这样可以提高程序的吞吐量。 >2. 假设功能产生并发几率极少,采用乐观锁版本机制对比, 如果有冲突 返回给用户错误的信息 ### 5.4.2、为什么需要锁(并发控制) 在多用户环境中,在同一时间可能会有多个用户更新相同的记录,这会产生冲突。这就是著名的并发性问题 - 丢失更新:一个事务的更新覆盖了其它事务的更新结果,就是所谓的更新丢失。例如:用户1把值从500改为8000,用户B把值从500改为200,则多人同时提交同一条记录,后提交的把之前的提交数据覆盖。 - 脏读:当一个事务读取其它完成一半事务的记录时,就会发生脏读。例如:用户A,B看到的值都是500,用户B把值改为200,用户A读到的值仍为500。 针对一种问题的解决方案,为解决问题而生的。解决什么问题呢?主要是解决丢失更新问题如下图理解 ![在这里插入图片描述](https://www.icode9.com/i/ll/?i=20210609010622964.png?,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3pob3VoZW5nemhl,size_16,color_FFFFFF,t_70) 为了解决这些并发带来的问题。 我们需要引入并发控制机制。 ### 5.4.3、MybatisPlus使用乐观锁 数据库层面:需要加个"version"字段,来控制脚本 ![在这里插入图片描述](https://www.icode9.com/i/ll/?i=20210609011236708.png?,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3pob3VoZW5nemhl,size_16,color_FFFFFF,t_70) 代码层面: 1、在对应的实体类中加version字段,并设置成下面这样 ```java @Version //这就是控制版本的 @TableField(fill = FieldFill.INSERT) //这个方便在添加的时候设置版本初始为1 private Integer version; //版本的字段 ``` 创建一个新的类,实现MetaObjectHandler自动填充,像创建时间,更新时间也可以在这操作。 ```java @Component public class MybatisPlusMetaObjectHandler implements MetaObjectHandler { @Override public void insertFill(MetaObject metaObject) { //这里的“version”就是指定的字段,设置初始值为1,之后每修改一次+1 this.setFieldValByName("version",1,metaObject); } @Override public void updateFill(MetaObject metaObject) { } } ``` 在创建一个配置类,开启一个乐观锁插件 ```java @Configuration @MapperScan("com.zhz.mapper")//这里就是你的mapper文件的包 public class MyBatisConfig { //乐观锁插件 @Bean public OptimisticLockerInterceptor optimisticLockerInterceptor(){ return new OptimisticLockerInterceptor(); } } ``` 接下来在做增加数据的时候,调用insert添加方法就可以了。 修改的时候呢,我们需要先查人后再做修改,因为我们为了防止问题的发生,需要先去查询版本号比对才进行后续操作!! ## 5.5、代码生成器 ```java package com.zhz.testcode; import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException; import com.baomidou.mybatisplus.core.toolkit.StringPool; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.baomidou.mybatisplus.generator.AutoGenerator; import com.baomidou.mybatisplus.generator.InjectionConfig; import com.baomidou.mybatisplus.generator.config.*; import com.baomidou.mybatisplus.generator.config.po.LikeTable; import com.baomidou.mybatisplus.generator.config.po.TableInfo; import com.baomidou.mybatisplus.generator.config.rules.DateType; import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy; import java.util.ArrayList; import java.util.List; import java.util.Scanner; /*** * @Author zhz * @Slogan 致敬大师,致敬未来的你 */ public class GeneratorApp { /** *

* 读取控制台内容 *

*/ public static String scanner(String tip) { Scanner scanner = new Scanner(System.in); StringBuilder help = new StringBuilder(); help.append("请输入" + tip + ":"); System.out.println(help.toString()); // 判断用户是否输入 if (scanner.hasNext()) { // 拿到输入内容 String ipt = scanner.next(); if (StringUtils.isNotBlank(ipt)) { return ipt; } } throw new MybatisPlusException("请输入正确的" + tip + "!"); } public static void main(String[] args) { String moduleName = scanner("模块名"); String tableName = scanner("表名(多个用,号分隔,或者按前缀(pms*))"); String prefixName = scanner("需要替换的表前缀"); // 代码生成器 AutoGenerator mpg = new AutoGenerator(); // 全局配置 GlobalConfig gc = new GlobalConfig(); // 获得当前项目的路径 String projectPath = System.getProperty("user.dir")+"/05_generator"; // 设置生成路径 gc.setOutputDir(projectPath + "/src/main/java"); // 作者 gc.setAuthor("zhz"); // 代码生成是不是要打开所在文件夹 gc.setOpen(false); // 生成Swagger2注解 gc.setSwagger2(true); // 会在mapper.xml 生成一个基础的 映射所有的字段 gc.setBaseResultMap(true); // 同文件生成覆盖 gc.setFileOverride(true); //gc.setDateType(DateType.ONLY_DATE) // 实体名:直接用表名 %s=表名 gc.setEntityName("%s"); // mapper接口名 gc.setMapperName("%sMapper"); // mapper.xml 文件名 gc.setXmlName("%sMapper"); // 业务逻辑类接口名 gc.setServiceName("%sService"); // 业务逻辑类实现类名 gc.setServiceName("%sImplService"); // 将全局配置设置到AutoGenerator mpg.setGlobalConfig(gc); // 数据源配置 DataSourceConfig dsc = new DataSourceConfig(); dsc.setUrl("jdbc:mysql://localhost:3307/mybatisplus?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&"); dsc.setDriverName("com.mysql.cj.jdbc.Driver"); dsc.setUsername("root"); dsc.setPassword("489773"); mpg.setDataSource(dsc); // 包配置 PackageConfig pc = new PackageConfig(); // 模块名 pc.setModuleName(moduleName); // 包名 pc.setParent("com.zhz"); // 完整的报名: com.zhz.pms mpg.setPackageInfo(pc); // 自定义配置 InjectionConfig cfg = new InjectionConfig() { @Override public void initMap() { // to do nothing } }; // 如果模板引擎是 velocity String templatePath = "/templates/mapper.xml.vm"; // 自定义输出配置 List focList = new ArrayList<>(); // 自定义配置会被优先输出 focList.add(new FileOutConfig(templatePath) { @Override public String outputFile(TableInfo tableInfo) { // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!! return projectPath + "/src/main/resources/mapper/" + pc.getModuleName() + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML; } }); cfg.setFileOutConfigList(focList); mpg.setCfg(cfg); // 配置模板 TemplateConfig templateConfig = new TemplateConfig(); // 把已有的xml生成置空 templateConfig.setXml(null); mpg.setTemplate(templateConfig); // 策略配置 StrategyConfig strategy = new StrategyConfig(); // 表名的生成策略:下划线转驼峰 pms_product -- PmsProduct strategy.setNaming(NamingStrategy.underline_to_camel); // 列名的生成策略:下划线转驼峰 last_name -- lastName strategy.setColumnNaming(NamingStrategy.underline_to_camel); //strategy.setSuperEntityClass("你自己的父类实体,没有就不用设置!"); strategy.setEntityLombokModel(true); // 在controller类上是否生成@RestController strategy.setRestControllerStyle(true); // 公共父类 //strategy.setSuperControllerClass("你自己的父类控制器,没有就不用设置!"); if(tableName.indexOf('*')>0){ // 按前缀生成表 strategy.setLikeTable(new LikeTable(tableName.replace('*','_'))); } else{ // 要生成的表名 多个用逗号分隔 strategy.setInclude(tableName); } // 设置表替换前缀 strategy.setTablePrefix(prefixName); // 驼峰转连字符 比如 pms_product --> controller @RequestMapping("/pms/pmsProduct") //strategy.setControllerMappingHyphenStyle(true); mpg.setStrategy(strategy); // 进行生成 mpg.execute(); } } ``` 6、介绍一款非常好用的idea插件 > MybatisCodeHelperNew 自行百度下载,使用,可以快速省略mybatisplus的代码生成操作,他可以直接操作数据库,自行了解 下面是本人的公众号:(有兴趣可以扫一下,文章会同步过去) ![在这里插入图片描述](https://www.icode9.com/i/ll/?i=20210429100355821.jpg#pic_center) >我是小白弟弟,一个在互联网行业的小白,立志成为一名架构师 >https://blog.csdn.net/zhouhengzhe?t=1
上一篇:Dubbo Wrapper 解析


下一篇:单层装饰器