mybatis-plus的使用

 

 

 


前言

本文采用Springboot集成mybatis-plus,记录了集成的全部的详细操作。希望大家看到之后一起交流学习


 

一、mybatis-plus是什么?

MyBatis­Plus(简称 MP)是一个 MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

33e10565060b3e075e4aa165d961d87a.png

愿景:

我们的愿景是成为 MyBatis 最好的搭档,就像 魂斗罗 中的 1P、2P,基友搭配,效率翻倍。

特性

  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑

  • 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作

  • 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求

  • 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错

  • 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题

  • 支持 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 操作智能分析阻断,也可自定义拦截规则,预防误操作

     

    官网:MyBatis-Plus

    注:

    ps:切记不可再在pom.xml文件中引入mybatis与mybatis-spring的maven依赖,这一点,mybatis-plus的官方文档中已经说明的很清楚了

二、使用步骤

1.快速入门

mybatis-plus 快速使用

  1. 导入相关依赖

<!--mysql驱动--><dependency>    <groupId>mysql</groupId>    <artifactId>mysql-connector-java</artifactId>    <scope>runtime</scope></dependency><!-- mybatis-plus不需要再额外引入mybatis了--><dependency>    <groupId>com.baomidou</groupId>    <artifactId>mybatis-plus-boot-starter</artifactId>    <version>3.4.2</version></dependency><!--lombok--><dependency>     <groupId>org.projectlombok</groupId>     <artifactId>lombok</artifactId>     <version>1.18.18</version></dependency>

 

2.创建数据表并编写Entity实体(刚入门测试,后续代码生成器可以全部生成controller、service、mapper、实体等都可以生成)

CREATE TABLE it_employee (  id INT(11) PRIMARY KEY auto_increment,emp_name VARCHAR(50),  emp_email VARCHAR(50),  gender CHAR(1),  age INT(10));INSERT INTO it_employee VALUES (NULL,'张三','zhangsan@163.com','0',18);INSERT INTO it_employee VALUES (NULL,'李四','lisi@163.com','1',21);INSERT INTO it_employee VALUES (NULL,'王五','wangwu@163.com','1',19);INSERT INTO it_employee VALUES (NULL,'赵六','zhaoliu@163.com','0',17);

实体采用的lombok

import com.baomidou.mybatisplus.annotation.*;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import org.springframework.stereotype.Component;import java.util.Date; /*  * MybatisPlus会默认使用实体类的类名到数据中找对应的表.  * 所以表名和实体类名不一致时需要使用@TableName */ @Component@Data@AllArgsConstructor@NoArgsConstructor@TableName(value = "it_employee")public class ItEmployee { /*  * @TableId:  * value: 指定表中的主键列的列名, 如果实体属性名与列名一致,可以省略不指定. * type: 指定主键策略.  */     @TableId(value = "id", type = IdType.AUTO)    private Integer id;    @TableField(value = "emp_name")    private String empName;    @TableField(value = "emp_email")    private String empEmail;    private String gender;    private Integer age;}

上面实体类中使用到了常用的注解,那我们先来介绍一下常用的注解

mybatis-plus中常用的注解

@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:序列主键策略

 

常用的就三个:

@TableName

@TableId

@TableField

查看更多注解以及详解,请移步至官网:

https://mybatis.plus/guide/annotation.html 

3.配置yml基本的数据库连接信息以及数据源设置,这里数据源采用的是druid

 

# 数据源spring:  datasource:    username: root    password: root    url: jdbc:mysql://localhost:3306/stupubdemo?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&autoReconnect=true&failOverReadOnly=false&maxReconnects=10    driver-class-name: com.mysql.jdbc.Driver    type: com.alibaba.druid.pool.DruidDataSourceserver:  port: 80

 

  1. 新建mappe进行操作

@Mapperpublic interface ItEmployeedao extends BaseMapper<ItEmployee> {}

 

BaseMapper<T>的介绍:

只需要继承BaseMapper就好无需编写其他方法,在使用Mybatis-Plus是,核心操作类是BaseMapper接口,其最终也是利用的Mybatis接口编程的实现机制,其默认提供

了一系列的增删改查的基础方法,并且开发人员对于这些基础操作不需要写SQL进行处理操作(Mybatis提供的机制就是 需要开发人员在mapper.xml中提供sql语句),那样我们可以猜测肯定是Mybatis-Plus完成了BaseMapper接口提供的 方法的SQL语句的生成操作

BaseMapper中提供的方法:

25b604d0fd02219aa0f286f7bfeacb52.png

 

 

import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.Constants; import org.apache.ibatis.annotations.Param; import java.io.Serializable; import java.util.Collection; import java.util.List; import java.util.Map;  /**  * Mapper 继承该接口后,无需编写 mapper.xml 文件,即可获得CRUD功能  * <p>这个 Mapper 支持 id 泛型</p>  *  * @author hubin  * @since 2016‐01‐23  */  public interface BaseMapper<T> extends Mapper<T> { /** * 插入一条记录 * * @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 wrapper 实体对象封装操作类(可以为 null) */63 int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper); /** * 删除(根据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<T> updateWrapper); /** * 根据 ID 查询 * * @param id 主键ID */ T selectById(Serializable id); /** * 查询(根据ID 批量查询) * * @param idList 主键ID列表(不能为 null 以及 empty) */ List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);   /**  * 查询(根据 columnMap 条件)  *  * @param columnMap 表字段 map 对象  */  List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);   /**  * 根据 entity 条件,查询一条记录  *  * @param queryWrapper 实体对象封装操作类(可以为 null)  */  T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);   /**  * 根据 Wrapper 条件,查询总记录数117 *  * @param queryWrapper 实体对象封装操作类(可以为 null)  */  Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);   /**  * 根据 entity 条件,查询全部记录  *  * @param queryWrapper 实体对象封装操作类(可以为 null)  */  List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);   /**  * 根据 Wrapper 条件,查询全部记录  *  * @param queryWrapper 实体对象封装操作类(可以为 null)  */  List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);   /**  * 根据 Wrapper 条件,查询全部记录  * <p>注意: 只返回第一个字段的值</p>  *  * @param queryWrapper 实体对象封装操作类(可以为 null)  */  List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);   /**  * 根据 entity 条件,查询全部记录(并翻页)  *  * @param page 分页查询条件(可以为 RowBounds.DEFAULT)  * @param queryWrapper 实体对象封装操作类(可以为 null)  */  <E extends IPage<T>> E selectPage(E page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);   /**  * 根据 Wrapper 条件,查询全部记录(并翻页)  *  * @param page 分页查询条件  * @param queryWrapper 实体对象封装操作类  */  <E extends IPage<Map<String, Object>>> E selectMapsPage(E page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);  }

 

 

  1. 测试类编写CRUD代码

 

@AutowiredItEmployeedao etEmployeedao;/** * 按id查询 */@Testvoid selectids(){    ItEmployee itEmployee = new ItEmployee();    itEmployee.setId(1);    System.out.println(etEmployeedao.getByIiEmployees(itEmployee));}/** * 按id批量查询 */@Testvoid selectidLists(){    ArrayList<Integer> integers = new ArrayList<>();    integers.add(1);    integers.add(2);    integers.add(3);    System.out.println(etEmployeedao.getByIiEmployeesList(integers));}/** * 按多个参数查询map */@Testvoid selectidMaps(){    HashMap<String, Object> map = new HashMap<>();    map.put("id","1");    map.put("age","18");    System.out.println(etEmployeedao.getByIiEmployeesMap(map));}/** * 新增 */@Testvoid  insert(){    ItEmployee itEmployee = new ItEmployee();    itEmployee.setAge(12);    itEmployee.setEmpEmail("123@163.com");    itEmployee.setEmpName("张三丰");    itEmployee.setGender("0");    int insert = etEmployeedao.insert(itEmployee);    if (insert>0){        System.out.println("新增成功!");    }}/** * 编辑 */@Testvoid  update(){    ItEmployee itEmployee = new ItEmployee();    itEmployee.setId(10);    itEmployee.setAge(10);    itEmployee.setEmpEmail("123456@163.com");    itEmployee.setEmpName("张丰三");    itEmployee.setGender("0");    int insert = etEmployeedao.update(itEmployee);    if (insert>0){        System.out.println("修改成功!");    }}/** * 删除 */@Testvoid  del(){    ItEmployee itEmployee = new ItEmployee();    itEmployee.setId(7);    int insert = etEmployeedao.del(itEmployee);    if (insert>0){        System.out.println("删除成功!");    }}

 

 

以上就是快速使用,基本操作都可以满足了,而且带条件的一些sql等都正好,上面是使用的mapper层直接使用,但是我们在实际开发中一般都会使用service,那么service层代码来了

2.复杂场景的使用

实现带service层的dai复杂条件的各种操作

  1. 以上快速使用的基础上,新建EmployeeService

 

import com.baomidou.mybatisplus.extension.service.IService;import com.wq.mybatisplusdemo.demo.entity.ItEmployee;public interface EmployeeService  extends IService<ItEmployee> {}

 

新建EmployeeServiceimpl

 

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;import com.wq.mybatisplusdemo.demo.dao.ItEmployeedao;import com.wq.mybatisplusdemo.demo.entity.ItEmployee;import com.wq.mybatisplusdemo.demo.service.EmployeeService;import org.springframework.stereotype.Service;/*** * service 实现类继承map提供通用的service基类 */@Servicepublic class EmployeeServiceimpl extends ServiceImpl<ItEmployeedao, ItEmployee> implements EmployeeService {}

 

就这么简单,service层就这样就行了,里面也有许多的方法直接使用

就可以准备测试了,但是在测试之前不得不提的条件构造器---Wrapper

97f98e198d770525f1d99ad2fb7c03f3.png

wrapper及其子类介绍

  1. Wrapper :条件构造抽象类,最顶端父类,抽象类中提供3个方法以及其他方法

(2)AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件,QueryWrapper(LambdaQueryWrapper) 和

UpdateWrapper(LambdaUpdateWrapper) 的父类用于生成 sql 的 where 条件, entity 属性也用于生成 sql 的 where

条件

(3)AbstractWrapper比较重要,里面的方法需要重点学习.

该抽象类提供的重要方法如下:(3)AbstractLambdaWrapper : Lambda 语法使用 Wrapper统一处理解析 lambda 获取 column。

(4)LambdaQueryWrapper :看名称也能明白就是用于Lambda语法使用的查询Wrapper

(5)LambdaUpdateWrapper : Lambda 更新封装Wrapper

(6)QueryWrapper : Entity 对象封装操作类,不是用lambda语法,自身的内部属性 entity 也用于生成 where 条件

该类的重要方法:

select方法

1 select(String... sqlSelect)

2 select(Predicate<TableFieldInfo> predicate)

3 select(Class<T> entityClass, Predicate<TableFieldInfo> predicate)

4 /*

5 例: select("id", "name", "age")

6 例: select(i ‐> i.getProperty().startsWith("test"))

7 */

(7)UpdateWrapper : Update 条件封装,用于Entity对象更新操作.

该类主要有以下三个重要的方法:

set方法1 set(String column, Object val)

2 set(boolean condition, String column, Object val)

3 /*

4 SQL SET 字段

5 例: set("name", "老李头")

6 例: set("name", "")‐‐‐>数据库字段值变为空字符串

7 例: set("name", null)‐‐‐>数据库字段值变为null

8 说明:boolean condition为控制该字段是否拼接到最终的sql语句中

9 */

setSql方法

1 setSql(String sql)

2 /*

3 设置 SET 部分 SQL

4 例: setSql("name = '老李头'")

5 */

3.2.带条件的crud实验

(1)带条件的查询

1

2 // 根据 entity 条件,查询一条记录

3 T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

4

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

6 List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

7

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

9 List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

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

11 List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

12

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

14 IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

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

16 IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWr

apper);

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

18 Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

(2)带条件的更新

1 @Test

2 void update() {

3 UpdateWrapper<Employee> updateWrapper=new UpdateWrapper<Employee>();

4 updateWrapper.eq("last_name", "lili").eq("age", 18).set("id", 100).set(false, "email", "000@qq.com");

5 empolyeeMapper.update(employee, updateWrapper);

6

7 }

8 }

其中set("id", 100).set(false, "email", "000@qq.com");中email属性设置为false,从执行的sql可以看出,设置为false不会

拼接到最终的执行sql中

(3)带条件的删除

1 // 根据 entity 条件,删除记录2 int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper);

3

4 // 根据 columnMap 条件,删除记录

5 int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);

 

测试service

 

  @ResourceEmployeeService employeeService;  /** * service方法根据id查询 */@Testvoid  servicetest(){    ItEmployee byId = employeeService.getById(1);    System.out.println(byId);}/*** * service方法他会首先查询,如查询没有则新增有则修改 */@Testvoid  saveOrUpdate(){    ItEmployee itEmployee = new ItEmployee(6,"111","123@qq.com","0",12,"1",null,new Date(),null);    employeeService.saveOrUpdate(itEmployee);}   /***     * 分页 需要先导入插件,需要新建config文件官网有提供     */    @Test    void page(){        IPage<ItEmployee> ipage= new Page<>(1,2);        IPage<ItEmployee> page=employeeService.page(ipage);        List<ItEmployee> pageRecords = page.getRecords();        System.out.println(page.getSize());        System.out.println(page.getTotal());        System.out.println(pageRecords);    }    /***     * xml分页     */    @Test    void xmlpage(){        IPage<ItEmployee> ipage=new Page<>(1,2);        List<ItEmployee> employees = itEmployeeService.getbygender(ipage, "1").getRecords();        for (ItEmployee employee : employees) {            System.out.println(employee);        }    }    @Test    void queryWrapper(){        QueryWrapper<ItEmployee> queryWrapper=new QueryWrapper<>();        queryWrapper                .select("emp_name","emp_email","age")                .eq("emp_name","李四");        System.out.println(employeeService.list(queryWrapper));    }    /***     *  根据 entity 条件,查询一条记录     */    @Test   void QueryWrappertest(){        ItEmployee employee = new ItEmployee();        employee.setEmpName("王五");        employee.setGender("0");        ItEmployee selectone = itEmployeeService.selectone(employee);        System.out.println(selectone);    }    /***     *  设置带多条件的修改     */    @Test    void UpdateWrappertest(){        ItEmployee employee = new ItEmployee();        employee.setEmpName("李四");        employee.setGender("1");        UpdateWrapper<ItEmployee> updateWrapper=new UpdateWrapper<>();//        查询条件emp_name为王五的和gender为1的 set为修改的 set(false, "email", "000@qq.com");中email属性设置为false,从执行的sql可以看出,设置为false不会//        拼接到最终的执行sql中//        updateWrapper.eq("emp_name",employee.getEmpName()).eq("gender",employee.getGender()).set("age",13).set(false,"email","wangwu@163.com");        updateWrapper                .lambda()                .set(ItEmployee::getAge,24)                .eq(ItEmployee::getEmpName,"李四")                .eq(ItEmployee::getGender,"1")                ;        int selectone = itEmployeeService.updateEntityparams(employee, updateWrapper);        if (selectone>0){            System.out.println("修改成功!");        }    }    /**     * 设置带多条件的删除     */    @Test    void  DelWrappertest(){     QueryWrapper<ItEmployee> employeeQueryWrapper=new QueryWrapper<>();     employeeQueryWrapper.eq("emp_name","王五").eq("gender","1");     int delEntityparams = itEmployeeService.DelEntityparams(employeeQueryWrapper);     if (delEntityparams>0){         System.out.println("删除成功!");     }    }

 

上面test类中有提到分页

他的分页需要新建config文件官网有提供的

 

@Configurationpublic class mybatisplusConfig {    //最新版    @Bean    public MybatisPlusInterceptor mybatisPlusInterceptor() {        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();        //分页插件        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));        return interceptor;    }    }

 

xml分页的使用

需要新建xml文件,有时候在业务场景比较复杂的情况下是可以自己编写xml进行分页编写的,他也保留mybatis作为半自动化的一款交互

 

<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" ><mapper namespace="com.wq.mybatisplusdemo.demo.dao.ItEmployeedao" ><select id="getbygender" resultType="com.wq.mybatisplusdemo.demo.entity.ItEmployee">    select * from it_employee where gender=#{gender}</select></mapper>

 

 

@Mapperpublic interface ItEmployeedao extends BaseMapper<ItEmployee> {    /***     * xml分页     * @param page     * @param     * @return     */    IPage<ItEmployee> getbygender(IPage page,String gender);}

3.mybatis-plus的扩展

扩展

全局ID生成策略

在全局配置文件中: 就不需要再每个Pojo主键上配置了

 

 mybatis‐plus:  global‐config:  db‐config:  id‐type: auto 

 

 

逻辑删除

物理删除: 在删除的时候直接将数据从数据库干掉DELTE

逻辑删除: 从逻辑层面控制删除,通常会在表里添加一个逻辑删除的字段比如 enabled 、is_delete ,数据默认是有效的(值为1),

当用户删除时将数据修改UPDATE 0, 在查询的时候就只查where enabled=1.

1. 需要添加逻辑删除的字段

2. 局部单表逻辑删除,需要在对应的pojo类加入对应的逻辑删除标识字段

@TableLogic // 代表逻辑删除

private Integer flag;

 

public Integer getFlag() {

return flag;

}

全局逻辑删除配置, 如果进行了全局逻辑删除配置并且指定了,就可以不用在每个pojo类中配置了@TableLogic

 

 

mapper-locations: classpath:/mappexxr/*.xml  global‐config:    db‐config:      logic-delete-field: flag # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)      logic-delete-value: 0 # 逻辑已删除值(默认为 1)      logic-not-delete-value: 1 # 逻辑未删除值(默认为 0)     id‐type: auto

 

 

当执行删除, 将会把逻辑删除字段进行修改当执行查询,会自动查询有效数据 where flag=1

执行 SQL 分析打印

 

 

<!--sql性能分析日志--><dependency>    <groupId>p6spy</groupId>    <artifactId>p6spy</artifactId>    <version>3.9.1</version></dependency>

 

 

添加p6spy : spy.properties

 

 #3.2.1以上使用  modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6Ou tageFactory  #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 秒24 outagedetectioninterval=2 

 

yml的连接信息需要改动

 

# 数据源spring:  datasource:    username: root    password: wq123    url: jdbc:p6spy:mysql://localhost:3306/stupubdemo?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&autoReconnect=true&failOverReadOnly=false&maxReconnects=10    driver-class-name: com.p6spy.engine.spy.P6SpyDriver #com.mysql.jdbc.Driver    type: com.alibaba.druid.pool.DruidDataSource

 

e6ad9737ffb2c8aac51d87a522adfc35.png

效果:

07738921dd8b2e67e4bf3128a263c186.png

数据安全保护

防止删库跑路

1.得到16位随机秘钥

 

 

@Test  void test(){// 生成 16 位随机 AES 密钥  String randomKey = AES.generateRandomKey();  System.out.println(randomKey);  }  运行结果的 : da12166c7db8a58f 

 

 

  1. 根据秘钥加密 数据库连接信息

 

 @Test  void test(){   String url = AES.encrypt("jdbc:mysql://localhost: 3306/mybatisplus?characterEncoding=utf8&useSSL=false &serverTimezone=UTC&" , "da12166c7db8a58f");  String uname = AES.encrypt("root" , "da12166c7db8a58f");  String pwd = AES.encrypt("123456" , "da12166c7db8a58f");  System.out.println(url);  System.out.println(uname);  System.out.println(pwd);11 } 把数据库的url user pwd等进行加密

 

 

 

3.修改生产的配置文件prod 注意要mpw:开头

 

 

# 数据源spring:  datasource:    username: mpw:uO+56CtZiH7WvW0Uttle0w==    password: mpw:+R96zspi9RpHpagOrFo7jw==    url: mpw:O97zTPbK6kRCnbNRHvNclC9A1eohqPwv40CKLaI+w678j4njvEsuoTuMkr/hXULdscaFPDQxxFRpLRELduLEsEHCOPEp6JV1JQi+vbgcQg95zCKQWC5PIyXvitTpDCJsIjwlH62hxTochApIXr+6HTNDXfXIj2aOeNMfy87jFx7Ax0fqc/NrAIIka4+Mh7EFeZdbbYvHq0dlzpmNkXhJCLF1upppRVhiSnupk3ANeQ0=    driver-class-name: com.mysql.jdbc.Driver    type: com.alibaba.druid.pool.DruidDataSourcelogging:  level:    root: info    com.tulingxueyuan: debugmybatis-plus:  global-config:    db-config:      logic-delete-field: enabled  #逻辑删除的字段      logic-delete-value: 0     #已删除数据      logic-not-delete-value: 1   #有效数据      id-type: autoserver:  port: 8010

 

  1. 在springboot启动类中添加测试方法

 

import com.wq.mybatisplusdemo.demo.service.EmployeeService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class DemoApplication {    static EmployeeService employeeService;    @Autowired    public void setEmployeeService(EmployeeService employeeService) {        DemoApplication.employeeService = employeeService;    }    public static void main(String[] args) {        SpringApplication.run(DemoApplication.class, args);        // 测试数据安全保护下 部署上线能否正确连接数据库        System.out.println(employeeService.getById(1));    }}

 

4.使用maven进行package进行打包

2835fe6d59cf81cd88750c502578d6a0.png

注意打包的时候你的本地运行还是开发环境,不然打不好的,

默认指向开发

97221cb71c9d632736e8b01ff3012aff.png

 

4.打开你打完包后的目录然后进行cmd 在部署的时候需要解密 java -jar xxxx.jar --mpw.key=你的16位随机秘钥 --spring.profiles.active=prod

3e5492403f5c1bbb5d37beb0ac00b0c6.png

 

查询出数据代表部署成功

 

乐观锁插件使用

第一:什么是乐观锁

悲观锁:悲观锁,正如其名,具有强烈的独占和排他特性。它指的是对数据被外界(包括本系统当前的其他事

务,以及来自外部系统的事务处理)修改持保守态度。因此,在整个数据处理过程中,将数据处于锁定状态。

假设功能并发量非常大,就需要使用synchronized来处理高并发下产生线程不安全问题, 会使其他线程进行挂起等待从

而影响系统吞吐量

乐观锁:乐观锁是相对悲观锁而言的,乐观锁假设数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正

式对数据的冲突与否进行检测,如果发现冲突了,则返回给用户错误的信息,让用户决定如何去做。乐观锁适用于读操作多的场

景,这样可以提高程序的吞吐量。 假设功能产生并发几率极少,采用乐观锁版本机制对比, 如果有冲突 返回给用户错

误的信息

第二:为什么需要锁(并发控制)

在多用户环境中,在同一时间可能会有多个用户更新相同的记录,这会产生冲突。这就是著名的并发性问题

丢失更新:一个事务的更新覆盖了其它事务的更新结果,就是所谓的更新丢失。例如:用户1把值从500改为

8000,用户B把值从500改为200,则多人同时提交同一条记录,后提交的把之前的提交数据覆盖。

脏读:当一个事务读取其它完成一半事务的记录时,就会发生脏读。例如:用户A,B看到的值都是500,用户B

把值改为200,用户A读到的值仍为500。

针对一种问题的解决方案,为解决问题而生的。解决什么问题呢?主要是解决丢失更新问题如下图理解

为了解决这些并发带来的问题。 我们需要引入并发控制机制。

第三:乐观锁使用MyBatisPlus的解决方式

由于锁这个字眼我们需要在数据库加个字段“

version”来控制版本

在类中加个属性

 

 @Version //这就是控制版本的  @TableField(fill = FieldFill.INSERT) //这个方便在添加的时候设置版本初始为1  private Integer version; //版本的字段 

 

 

下面这个也是MyBatisPlus的一个插件 添加配置文件

 

@Configurationpublic class mybatisplusConfig {    //最新版    @Bean    public MybatisPlusInterceptor mybatisPlusInterceptor() {        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();        //分页插件        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));//        // 乐观锁的插件        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());        return interceptor;    }}

 

只需要实现MetaObjectHandler就可以了

 

@Componentpublic class MyMetaObjectHandler implements MetaObjectHandler {    @Override    public void insertFill(MetaObject metaObject) {        // 插入时:创建时间和修改时间        this.setFieldValByName("createDate",new Date(),metaObject);        this.setFieldValByName("modifyDate",new Date(),metaObject);        //这里的“version”就是指定的字段,设置初始值为1,之后每修改一次+1        this.setFieldValByName("version",1,metaObject);    }    @Override    public void updateFill(MetaObject metaObject) {        this.setFieldValByName("modifyDate",new Date(),metaObject);    }}

 

 

接下来在做增加数据的时候,调用insert添加方法就可以了。

修改的时候呢,我们需要先查人后再做修改,因为我们为了防止问题的发生,需要先去查询版本号比对才进行后续操 作!!

test

 

/*** * 乐观锁 */@Testvoid testCAs(){    ItEmployee employee =employeeService.getById(1);    employee.setAge(100);    ItEmployee employee2 =employeeService.getById(1);    employee2.setAge(80);    if (employeeService.updateById(employee)){        System.out.println("更新成功");    }    if (!employeeService.updateById(employee2)){        System.out.println("请稍后重试!");    }}

 

效果:

016b61e77e9371321f373f32d6e41825.png

 

 

4.代码生成器

 

新建类

public static void main(String[] args) {        String userName="root";        String password="wq123";        String url="jdbc:mysql://localhost:3306/stupubdemo?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&autoReconnect=true&failOverReadOnly=false&maxReconnects=10";        FastAutoGenerator.create(url, userName, password)                .globalConfig(builder -> {                    builder.author("Qi.Wang") // 设置作者                            .enableSwagger() // 开启 swagger 模式                            .fileOverride() // 覆盖已生成文件                            .outputDir("D://logs")//生成的项目目录                            .dateType(DateType.TIME_PACK)                            .commentDate("yyyy-MM-dd"); // 指定输出目录                })                .packageConfig(builder -> {                    builder.parent("com.wq.mybatisplusdemo.demo") // 设置父包名                            .moduleName("wms") // 设置父包模块名                            .entity("entity")                            .service("service")                            .serviceImpl("service.impl")                            .mapper("mapper")                            .xml("mapper.xml")                            .controller("controller")                            .other("other")                            .pathInfo(Collections.singletonMap(OutputFile.mapperXml, "D:wqprojectjavaprojectspringbootspringboot_mybatis-plusspringboot_mybatisplussrcmain
esourcesmappexxr")); // 设置mapperXml生成路径                })                .strategyConfig(builder -> {                    builder.addInclude("it_employee") // 设置需要生成的表名                            .addTablePrefix("it_"); // 设置过滤表前缀                })                .strategyConfig(builder -> {                    builder.entityBuilder()                            .enableTableFieldAnnotation()                            .versionColumnName("version")                            .versionPropertyName("version")                            .logicDeleteColumnName("flag")                            .logicDeletePropertyName("flag")                            .addSuperEntityColumns("id","create_by","create_date","modify_by","modify_date")                            .idType(IdType.AUTO)                            .formatFileName("%sEntity");                })                .strategyConfig(builder -> {                    builder.serviceBuilder()                            .formatServiceFileName("%sService")                            .formatServiceImplFileName("%sServiceImpl");                })                .strategyConfig(builder -> {                    builder.mapperBuilder()                            .enableMapperAnnotation();                })//                .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板                .execute();    }

具体还想生成一些内容可参考官网

效果:

controller、entity mapper service等都帮我生成好了真是一个利器呀!

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAbTBfNjQzNzEwMjU=,size_20,color_FFFFFF,t_70,g_se,x_16

 

 

 

 

 


总结

以上就是今天要讲的内容了,钟对mybatisplus的具体操作使用,需要内容源码的可私信我,就到这了有问题再评论区提出来多交流学习。

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值