MyBatis-Plus(狂神说:3.0.5版)

MyBatis-Plus

1.Mybatis-Plus概述

  • 需要的基础:MyBatis、Spring、SpringMVC
  • CRUD自动化完成

简介

  • MyBatis-Plus (官网) 简化MyBatis

特性

  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
  • 损耗小:启动即会自动注入基本 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 操作智能分析阻断,也可自定义拦截规则,预防误操作

2.快速入门

  • 地址:快速开始 | MyBatis-Plus (baomidou.com)

  • 使用第三方组件:

    • 1.导入对应依赖
    • 2.研究依赖如何配置
    • 3.代码如何编写
    • 4.提高扩展技术能力!

步骤

  • 1.创建数据库Mybatis_Plus
  • 2.创建user表
DROP TABLE IF EXISTS user;

CREATE TABLE user
(
    id BIGINT(20) NOT NULL COMMENT '主键ID',
    name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
    age INT(11) NULL DEFAULT NULL COMMENT '年龄',
    email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
    PRIMARY KEY (id)
);
DELETE FROM user;

INSERT INTO user (id, name, age, email) VALUES
(1, 'Jone', 18, '[email protected]'),
(2, 'Jack', 20, '[email protected]'),
(3, 'Tom', 28, '[email protected]'),
(4, 'Sandy', 21, '[email protected]'),
(5, 'Billie', 24, '[email protected]');
-- 真实开发中,version(乐观锁)、deleted(逻辑删除)、gmt_create、gmt_modified
  • 3.编写项目,初始化项目,使用SpringBoot初始化

  • 4.导入依赖


<dependency>
    <groupId>mysqlgroupId>
    <artifactId>mysql-connector-javaartifactId>
dependency>

<dependency>
    <groupId>org.projectlombokgroupId>
    <artifactId>lombokartifactId>
dependency>


<dependency>
    <groupId>com.baomidougroupId>
    <artifactId>mybatis-plus-boot-starterartifactId>
    <version>3.0.5version>
dependency>
  • 说明:mybatis-plus节省代码,尽量不要同时导入mybatis和mybatis-plus,版本冲突。
  • 5.连接数据库
#mysql 5 驱动不同 com.mysql.jdbc.Driver


#mysql 8 驱动不同 com.mysql.cj.jdbc.Driver 需要增加时区配置 serverTimezone=GMT%2B8
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?serverTiemzoe=UFC&useUnicode=true&characterEncoding=utf-8
  • 6.传统方式pojo-dao(连接mybatis,配置mapper.xml文件)-service-controller

  • 6.使用mybatis-plus

    • pojo

      @Data
      @AllArgsConstructor
      @NoArgsConstructor
      public class User{
          private Long id;
          private String name;
          private Integer age;
          private String email;
      }
      
    • mapper接口

      @Reposirory //代表持久层
      public interface UserMapper extends BaseMapper<User>{
          // 所有的CRUD操作都已编写完成
          //不需要像以前一样写配置文件
      }
      
    • 注意:

      • 需要在主启动类上去扫描mapper包下的所有接口@MapperScan("com.ggj.mapper")
    • 测试类

      @SpringBootTest
      class MybatisPlusApplicationTests{
          //继承BaseMapper,所有的方法都来自自己的父类
          //也可以自己编写扩展方法
          @Autowried
          private UserMapper userMapper;
          @Test
          void contextLoads(){
              //参数是一个wrapper,条件构造器,先不用 null
              //查询全部用户
              List<User> users = userMapper.selectList(null);
              users.forEach(System.out::println);
          }
      	
      }
      

3.配置日志

  • 所有sql不可见,希望知道sql怎么执行,所以必须要看日志。
#配置日志
#StdOutImpl 输出到控制台
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl 

4.CRUD扩展

//测试插入
@Test
public void testInsert(){
    User user = new User();
    user.setName("ggj");
    user.setAge(16);
    user.setEmail("[email protected]");
    
    int result = userMapper.insert(user);
    System.out.println(result);
    System.out.println(user);//发现id会自动回填
}

数据库插入的id的默认值:全局的唯一id

4.1.主键生成策略

默认ID_WORKER全局唯一id

  • 分布式唯一 ID 的 7 种生成方案-阿里云开发者社区 (aliyun.com)

  • 雪花算法:Twitter的snowflake算法

    • 1位符号位:
      • 由于 long 类型在 java 中带符号的,最高位为符号位,正数为 0,负数为 1,且实际系统中所使用的ID一般都是正数,所以最高位为 0。
    • 41位时间戳(毫秒级):
      • 需要注意的是此处的 41 位时间戳并非存储当前时间的时间戳,而是存储时间戳的差值(当前时间戳 - 起始时间戳),这里的起始时间戳一般是ID生成器开始使用的时间戳,由程序来指定,所以41位毫秒时间戳最多可以使用 (1 << 41) / (1000x60x60x24x365) = 69年
    • 10位数据机器位:
      • 包括5位数据标识位和5位机器标识位,这10位决定了分布式系统中最多可以部署 1 << 10 = 1024 s个节点。超过这个数量,生成的ID就有可能会冲突。
    • 12位毫秒内的序列:
      • 这 12 位计数支持每个节点每毫秒(同一台机器,同一时刻)最多生成 1 << 12 = 4096个ID
  • 加起来刚好64位,为一个Long型。

    • 优点:高性能,低延迟,按时间有序,一般不会造成ID碰撞
    • 缺点:需要独立的开发和部署,依赖于机器的时钟

主键自增

  • 配置主键自增
    • 1.实体类字段上@TableId(IdType.AUTO)
    • 2.数据库字段一定要选中自增
    • 3.测试插入

其他源码解释

public enum IdType{
    AUTO(0), //数据库id自增
    NONE(1), //未设置主键
    INPUT(2),//手动输入
    ID_WORKER(3),//默认的全局唯一id
    UUID(4),//全局唯一id,uuid
    ID_WORKER_STR(5);//ID_WORKER(3) 字符串表示法
}

4.2.更新操作

//测试更新
@Test
public void testUpdate(){
    User user = new User();
    //通过条件自动拼接动态sql
    user.setId(1)
    user.setName("GGJ");
    user.setAge(16);
    user.setEmail("[email protected]");
    //注意:updateById 参数是一个对象User
    int i = userMapper。updateById(user);
    System.out.println(i);
}

4.3自动填充

  • 创建时间、修改时间!这些操作需要自动化完成
  • 阿里巴巴开发手册:所有数据库表:gmt_create 、gmt_modified,几乎所有的表都要配置上,而且需要自动化

方式一:数据库级别(工作中不建议(不允许))

  • 1.在表中新增字段:create_time、update_time(选择更新)

  • 2.测试插入方法,需先把实体类同步

private Date createTime;
private Date updateTime;
  • 3.查看更新结果

方式二:代码级别

  • 1.删除数据库默认值,更新操作
  • 2.实体类字段属性需要增加注解
//字段添加填充内容,现在用LocalDateTime
@TableField(fill = FieldFill.INSERT)
private Date createTime;

@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
  • 3.编写处理器处理注解
@Slf4j
@Component //不要忘记把处理器加入到IOC容器中
public class MyMetaObjectHandler implements MetaObjectHandler {

    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("start insert fill ....");
        this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐使用)
        // 或者
        this.strictInsertFill(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) {
        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)
    }
}
  • 4.测试插入
  • 5.测试更新,观察更新时间

4.4乐观锁

乐观锁:十分乐观,总是认为不会出现问题,无论干什么不去上锁,如果出现了问题,再次更新值从测试

悲观锁:十分悲观,总是认为总是出现问题,无论干什么都会区上锁,再去操作

  • 乐观锁实现方式:
    • 取出记录时,获取当前 version
    • 更新时,带上这个 version
    • 执行更新时, set version = newVersion where version = oldVersion
    • 如果 version 不对,就更新失败

测试MP的乐观锁插件

  • 1.给数据库中增加version字段
  • 2.给实体类加对应字段
@version
private Integer version;

4.5查询操作

//测试查询
@Test
public void testSelectById(){
    User user = userMapper.selectById(1L);
    System.out,println(user)
}

//测试批量查询
@Test
public void testSelectByBatchId(){
    List<User> users = userMapper.selectBatchIds(Arrays.asList(1,2,3));
    users.forEach(System.out::println);
}
//按条件查询之一 使用map操作
public void testSelectByBatchIds(){
    HashMap<String,Object> map = new HashMap<>();
    //自定义查询
    map.put("name","ggj");
    map.put("age","16");
    List<User> users = userMapper.selectByMap(map);
    users.forEach(System.out::println);
}

4.6 分页查询

  • 1.原始的limit分页
  • 2.pageHelper第三方插件
  • 3.MP内置分页

如何使用?

  • 1.配置拦截器组件
    @Bean
    public PaginationInterceptor paginationInterceptor(){
        return new PaginationInterceptor();
    }
    
    
    /**
     * 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2));
        return interceptor;
    }

    @Bean
    public ConfigurationCustomizer configurationCustomizer() {
        return configuration -> configuration.setUseDeprecatedExecutor(false);
    }
  • 2.直接使用Page对象
//测试分页查询
@Test
public void testPage(){
    //参数一:当前页
    //参数二:页面大小
    Page<User> page = new Page<>(2,5);
    userMapper.selectPage(page,null);
    
    page.getRecords().forEach(System.out::println);
    System.out.println(page.getTotal);
}

4.7删除操作

  • 1.根据id删除记录
//测试删除
@Test
public void testDeleteById(){
    userMapper.deleteById(id);
}
//通过id批量删除
@Test
public void testDeleteBatchId(){
    userMapper.deleteBatchIds(Arrays.asList(id,id,id...));
}
//通过map批量删除
@Test
public void testDeleteMap(){
    HashMap<String,Object> map = new HashMap<>();
    map.put("name","ggj");
    userMapper.deleteByMap(map);
}

4.8逻辑删除

物理删除:从数据库中直接移除

逻辑删除:没有从数据库中直接移除,而是通过一个变量让其失效

  • 例如:管理员可以查看被删除的字段
    • 防止数据丢失,类似于回收站
  • 1.在数据库中增加一个deleted字段
  • 2.实体类中增加属性
@TableLogic //逻辑删除
private Integer deleted;
  • 3.配置
  • 步骤一:
mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: flag # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
      logic-delete-value: 1 # 逻辑已删除值(默认为 1)
      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
  • 步骤二:
//逻辑删除组件(高版本已经不需要了,只需要注解)
@Bean
public ISqlInjector sqlInjector(){
    return new LogicSqlInjector();
}
  • 测试删除
    • 运行更新操作,并不是删除操作
    • 查询的时候会自动过滤被逻辑删除的字段

以上所有的CRUD操作及扩展,需精通

5.性能分析插件

5.1 3.0.5版

  • 平时会遇见慢sql,测试,druid…
  • 作用:用于输出每条 sql 语句的执行时间的插件
  • MP提供性能分析插件,超时停止运行

该插件3.2.0以上版本移除。官方推荐使用第三方扩展,执行SQL分析打印的功能

  • 1.导入插件
/**
     * sql执行效率插件
     * @return
     */
@Bean
@Profile({"dev", "test"})
public PerformanceInterceptor performanceInterceptor() {
    PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
    //设置SQL执行的最大时间,如果超过了则不执行
    performanceInterceptor.setMaxTime(100);
    //是否开启格式化支持
    performanceInterceptor.setFormat(true);
    return  performanceInterceptor;
}
  • 配置springboot环境为dev或者test环境!
#设置开发环境 
spring.profiles.active=dev
  • 2.测试使用
  • 使用性能分析插件,可以提高效率。

5.2 3.5.2版

  • Maven:

  p6spy
  p6spy
  最新版本

  • Gradle:
compile group: 'p6spy', name: 'p6spy', version: '最新版本'
  • application.yml 配置:
spring:
  datasource:
    driver-class-name: com.p6spy.engine.spy.P6SpyDriver
    url: jdbc:p6spy:h2:mem:test
    ...
  • 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

6.条件构造器

  • 1.测试一
@Test
void contextLoads() {
    //查询name不为空的用户,并且邮箱不为空,年龄大于等于12
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper
            .isNotNull("name")
            .isNotNull("email")
            .ge("age",12);
    userMapper.selectList(wrapper).forEach(System.out::println);
}
  • 2.测试二
@Test
void contextLoads() {
    //查询name为ggj
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.eq("name","ggj");
    User user = userMapper.selectOne(wrapper);//查询一个数据,出现多个结果使用List或者Map
    System.out.println(user);
}
  • 3.测试三
@Test
void contextLoads() {
    //查询name不为空的用户,并且邮箱不为空,年龄大于等于12
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.between("age",18,20);//区间
    Integer count = userMapper.selectCount(wrapper);//查询结果数
    System.out.println(count);
}
  • 4.测试四
//模糊查询
void contextLoads() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper
            .notLike("name","j")
            .likeRight("email","@")
            .likeLeft("age",1);
    List<Map<String,Object>> maps = userMapper.selectMaps(wrapper);
    maps.forEach(System.out::print);
}
  • 5.测试五
void contextLoads(){
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    //从id区间中获取id
    wrapper.inSql("id","select id from user where id <3");
    List<Object> objects = userMapper.selectObjs(wrapper);
    objects.forEach(System.out::print);
}
  • 6.测试六
void contextLoads(){
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    //从id进行排序
    wrapper.orderByDesc("id");
    List<User> users = userMapper.selectList(wrapper);
    users.forEach(System.out::print);
}

7.代码自动生成器

java.version Java运行时环境版本
java.vendor Java运行时环境供应商
java.vendor.url Java供应商的 URL
java.home Java安装目录
java.vm.specification.version Java虚拟机规范版本
java.vm.specification.vendor Java虚拟机规范供应商
java.vm.specification.name Java虚拟机规范名称
java.vm.version Java虚拟机实现版本
java.vm.vendor Java虚拟机实现供应商
java.vm.name Java虚拟机实现名称
java.specification.version Java运行时环境规范版本
java.specification.vendor Java运行时环境规范供应商
java.specification.name Java运行时环境规范名称
java.class.version Java类格式版本号
java.class.path Java类路径
java.library.path 加载库时搜索的路径列表
java.io.tmpdir 默认的临时文件路径
java.compiler 要使用的 JIT 编译器的名称
java.ext.dirs 一个或多个扩展目录的路径
os.name 操作系统的名称
os.arch 操作系统的架构
os.version 操作系统的版本
file.separator 文件分隔符(在 UNIX 系统中是“/”)
path.separator 路径分隔符(在 UNIX 系统中是“:”)
line.separator 行分隔符(在 UNIX 系统中是“/n”)
user.name 用户的账户名称
user.home 用户的主目录
user.dir 用户的当前工作目录
package com.ggj.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.po.TableFill;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import org.omg.CORBA.DATA_CONVERSION;

import java.util.ArrayList;

public class codeConfig {
    public static void main(String[] args) {
        //构建代码自动生成器对象
        AutoGenerator mpg = new AutoGenerator();
        //配置策略
        //1.全局配置
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir");
        gc.setOutputDir(projectPath+"/src/main/java");
        gc.setAuthor("ggj");
        gc.setOpen(false);//open dir
        gc.setFileOverride(false);//是否覆盖
        gc.setServiceName("%Service");//去Service的I前缀
        gc.setIdType(IdType.ID_WORKER);
        gc.setDateType(DateType.ONLY_DATE);
        gc.setSwagger2(true);
        mpg.setGlobalConfig(gc);
        //2.设置数据源
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/mybatis_plus?serverTiemzoe=UFC&useUnicode=true&characterEncoding=utf-8");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("root");
        dsc.setDbType(DbType.MYSQL);
        mpg.setDataSource(dsc);
        //3.包的配置
        PackageConfig pc = new PackageConfig();
        pc.setModuleName("blog");
        pc.setParent("com.ggj");
        pc.setEntity("entity");
        pc.setMapper("mapper");
        pc.setService("service");
        pc.setController("controller");
        mpg.setPackageInfo(pc);
        //4.策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setInclude("user");//设置要映射的表名
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
//        strategy.setSuperEntityClass("父类实体,没有不用设置");
        strategy.setEntityLombokModel(true);//自动lombok
//        strategy.setRestControllerStyle(true);
        strategy.setLogicDeleteFieldName("deleted");
        //自动填充策略
        TableFill gmtCreate = new TableFill("gmt_create", FieldFill.INSERT);
        TableFill gmtModified = new TableFill("gmt_modified", FieldFill.INSERT_UPDATE);
        ArrayList<TableFill> tableFills = new ArrayList<>();
        tableFills.add(gmtCreate);
        tableFills.add(gmtModified);
        strategy.setTableFillList(tableFills);
        //乐观锁
        strategy.setVersionFieldName("version");
        strategy.setControllerMappingHyphenStyle(true);//localhost:8080/hello_id_20
        mpg.setStrategy(strategy);
        mpg.execute();//执行
    }
}

你可能感兴趣的:(mybatis,java,mysql)