测试


MyBatis-Plus

官网

https://mp.baomidou.com/

快速入门

  • https://mp.baomidou.com/guide/

本质

就是mybatis的升级,只是在mybatis的基础上做了提升,支持完全的向下兼容

这个图可以很好的解释

开始入门Demo

导入依赖

  •                      ;com.baomidou          ;mybatis-plus-boot-starter          ;3.0.5      ;
    
  •     只导入这一个依赖,不要再导入mybatis了,避免版本冲突
    

连接数据库

  • 导入mysql依赖

    •                     ;mysql          ;mysql-connector-java      ;
      
  • 配置application.properties

    • 数据库配置spring.datasource.url=jdbc:mysql://localhost:3306/sbs?useSSL=falseamp;useUnicode=trueamp;characterEncoding=utf-8amp;serverTimezone=UTCspring.datasource.username=root # 时区配置,mysql8必须配置spring.datasource.password=rootspring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver # 这是mysql8的版本驱动,完全支持向下兼容

编写pojo类

  • package com.xiaoye.entity;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import java.time.LocalDateTime;/** * @author 小也 * @create 2021/4/22 13:53 */@Data@AllArgsConstructor@NoArgsConstructorpublic class SysUser { private Long id; private String username; private String password; private String nickname; private Long gender; private Long age; private Long clazzId; private Long isDelete; private Long version; private LocalDateTime bjtCreate; private LocalDateTime bjtModified; }

编写mapper接口

  • package com.xiaoye.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;import com.xiaoye.entity.SysUser;import org.apache.ibatis.annotations.Mapper;/** * @author 小也 * @create 2021/4/22 13:56 */@Mapperpublic interface SysUserMapper extends BaseMapper; { //只要集成了BaseMapper这个父类就可以直接使用很多的单表操作了 }

    • BaseMapper

      • //// Source code recreated from a .class file by IntelliJ IDEA// (powered by FernFlower decompiler)//package com.baomidou.mybatisplus.core.mapper;import com.baomidou.mybatisplus.core.conditions.Wrapper;import com.baomidou.mybatisplus.core.metadata.IPage;import java.io.Serializable;import java.util.Collection;import java.util.List;import java.util.Map;import org.apache.ibatis.annotations.Param;public interface BaseMapper; { int insert(T var1); int deleteById(Serializable var1); int deleteByMap(@Param("cm") Map; var1); int delete(@Param("ew") Wrapper; var1); int deleteBatchIds(@Param("coll") Collection<? extends Serializable>; var1); int updateById(@Param("et") T var1); int update(@Param("et") T var1, @Param("ew") Wrapper; var2); T selectById(Serializable var1); List; selectBatchIds(@Param("coll") Collection<? extends Serializable>; var1); List; selectByMap(@Param("cm") Map; var1); T selectOne(@Param("ew") Wrapper; var1); Integer selectCount(@Param("ew") Wrapper; var1); List; selectList(@Param("ew") Wrapper; var1); List;>; selectMaps(@Param("ew") Wrapper; var1); List; selectObjs(@Param("ew") Wrapper; var1); IPage; selectPage(IPage; var1, @Param("ew") Wrapper; var2); IPage;>; selectMapsPage(IPage; var1, @Param("ew") Wrapper; var2);}

在主启动类上扫描mapper接口

  • package com.xiaoye;import org.mybatis.spring.annotation.MapperScan;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;/** * @author 22944 */@SpringBootApplication@MapperScan("com.xiaoye.mapper")public class Springboot04MybatisPlusApplication { public static void main(String[] args) { SpringApplication.run(Springboot04MybatisPlusApplication.class, args); }}

开启mybatis-plus的日志

  • 配置application.properties配置文件

    • 开启日志输出mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

测试一波

  • 测试代码

    • package com.xiaoye;import com.baomidou.mybatisplus.core.conditions.Wrapper;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;import com.xiaoye.entity.SysUser;import com.xiaoye.mapper.SysUserMapper;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import java.util.List;@SpringBootTestclass Springboot04MybatisPlusApplicationTests { @Autowired private SysUserMapper sysUserMapper; @Test void contextLoads() { QueryWrapper; wrapper = new QueryWrapper<>;(); wrapper.eq("id",1).like("nickname","ye").le("age",30).ge("gender","1"); List; users = sysUserMapper.selectList(wrapper); for (SysUser user : users) { System.out.println(user.toString()); } }}
  • QueryWrapper

    • QueryWrapper是mybatis-plus自带的条件查询器,我们可以使用这个QueryWrapper来构造查询条件

    • eq("数据库列名","值")

      • 要求某行的该列值和传入的值完全相等才能查出来
    • like("数据库列名","值")

      • 要求某行的该列值包含传入的值才能查出来
    • le("数据库列名","值")

      • 要求某行的该列值小于等于传入的值才能查出来
    • ge("数据库列名","值")

      • 要求某行的该列值大于等于传入的值才能查出来
  • 测试结果

    • mybatis-plus会帮我们自动生成简单的sql并拼接,对于单表操作可以说很友好了,但是对于多表联查还是需要自己手动写的

乐观锁

简介

  • 当要更新一条记录的时候,希望这条记录没有被别人更新

乐观锁实现方式

  • 取出记录时,获取当前version
  • 更新时,带上这个version
  • 执行更新时, set version = newVersion where version = oldVersion
  • 如果version不对,就更新失败

实际操作一下

  • 实体类中需要在相对应得字段上添加@Version注解

  • 编写配置类(来自官网)

    • // Spring Boot 方式@Configuration@MapperScan("按需修改")public class MybatisPlusConfig { /** * 旧版 / @Bean public OptimisticLockerInterceptor optimisticLockerInterceptor() { return new OptimisticLockerInterceptor(); } /* * 新版 */ @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor(); mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); return mybatisPlusInterceptor; }}
  • 测试乐观锁

    • 普通情况下(单线程操作)

      • @Test    void OptimisticLockTest(){        //1、查询用户,拿到版本号        SysUser user = sysUserMapper.selectById(1);        //2、执行更新操作        user.setAge(18);        sysUserMapper.updateById(user);    }
        
      • 结果
        
    • 多线程情况下

      • @Test    void OptimisticLockTest(){        //线程A        SysUser user1 = sysUserMapper.selectById(1);        user1.setAge(18);        //线程B执行        SysUser user2 = sysUserMapper.selectById(1);        user2.setAge(30);        sysUserMapper.updateById(user2);        //线程A继续执行更新操作        sysUserMapper.updateById(user1);    }
        
      • 结果
        

分页查询

我们首先想到的应该是学过的limit关键字,在数据库层面实现分页查询

现在我们使用mybatis-plus自带的分页插件来实现分页查询

简单的Page实现

  • 代码

    • @Test    void limitPageTest(){        Page; page = new Page<>;(2,1);        sysUserMapper.selectPage(page,null);        page.getRecords().forEach(System.out::println);    }
      
  • 效果

    • 一共三条记录,现在显示页面大小为1,第二页的记录

自动代码生成

AutoGenerator类

可能报错

  • 导入依赖即可

    •                                 ;org.apache.velocity              ;velocity-engine-core              ;2.0          ;
      

源码

  • package com.xiaoye.util;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 java.util.ArrayList;/** * @author 小也 * @create 2021/4/22 19:37 */public class AutoCoding { private static final String DATA_SOURCE_DRIVER = "com.mysql.cj.jdbc.Driver"; private static final String DATA_SOURCE_URL = "jdbc:mysql://localhost:3306/sbs?useSSL=falseamp;useUnicode=trueamp;characterEncoding=utf-8amp;serverTimezone=UTC"; private static final String DATA_SOURCE_USERNAME = "root"; private static final String DATA_SOURCE_PASSWORD = "root"; private static final String MODULE_NAME = "sys"; private static final String MODULE_PARENT_NAME = "com.xiaoye"; private static final String TABLE_NAME = "sys_user"; private static final String LOGIC_DELETE_NAME = "is_delete"; private static final String GMT_CREATE = "bjt_create"; private static final String GMT_MODIFIED = "bjt_modified"; private static final String OPTIMISTIC_LOCK_NAME = "version"; public static void main(String[] args) { //创建自动代码生成对象 AutoGenerator autoGenerator = new AutoGenerator(); //1、全局配置 GlobalConfig globalConfig = new GlobalConfig(); //设置作者 globalConfig.setAuthor("xiaoye"); //获取当前项目路径 String properties = System.getProperty("user.dir"); //设置输出路径 globalConfig.setOutputDir(properties + "/src/main/java"); //不要打开资源管理器 globalConfig.setOpen(false); //不覆盖原文件 globalConfig.setFileOverride(false); //service接口不带I前缀 globalConfig.setServiceName("%sService"); //设置id自增 globalConfig.setIdType(IdType.AUTO); //设置时间格式 globalConfig.setDateType(DateType.TIME_PACK); //配置swagger2文档 globalConfig.setSwagger2(true); //设置mapper.xml生成一个RestMap globalConfig.setBaseResultMap(true); autoGenerator.setGlobalConfig(globalConfig); //2、数据源配置 DataSourceConfig dataSourceConfig = new DataSourceConfig(); dataSourceConfig.setDbType(DbType.MYSQL); dataSourceConfig.setDriverName(DATA_SOURCE_DRIVER); dataSourceConfig.setUrl(DATA_SOURCE_URL); dataSourceConfig.setUsername(DATA_SOURCE_USERNAME); dataSourceConfig.setPassword(DATA_SOURCE_PASSWORD); autoGenerator.setDataSource(dataSourceConfig); //3、生成包配置 PackageConfig packageConfig = new PackageConfig(); packageConfig.setModuleName(MODULE_NAME); packageConfig.setParent(MODULE_PARENT_NAME); packageConfig.setEntity("entity"); packageConfig.setMapper("mapper"); packageConfig.setService("service"); packageConfig.setController("controller"); autoGenerator.setPackageInfo(packageConfig); //4、策略配置 StrategyConfig strategyConfig = new StrategyConfig(); //要映射的表名 strategyConfig.setInclude(TABLE_NAME); //下划线转驼峰命名 strategyConfig.setNaming(NamingStrategy.underline_to_camel); //下划线转驼峰命名 strategyConfig.setColumnNaming(NamingStrategy.underline_to_camel); //开启lombok strategyConfig.setEntityLombokModel(true); //开启逻辑删除名字 strategyConfig.setLogicDeleteFieldName(LOGIC_DELETE_NAME); //自动填充设置 TableFill bjtCreate = new TableFill(GMT_CREATE, FieldFill.INSERT); TableFill bjtModified = new TableFill(GMT_MODIFIED, FieldFill.INSERT_UPDATE); ArrayList; tableFieldList = new ArrayList<>;(); tableFieldList.add(bjtCreate); tableFieldList.add(bjtModified); strategyConfig.setTableFillList(tableFieldList); //乐观锁配置 strategyConfig.setVersionFieldName(OPTIMISTIC_LOCK_NAME); //restFul风格 strategyConfig.setRestControllerStyle(true); //下划线url strategyConfig.setControllerMappingHyphenStyle(true); autoGenerator.setStrategy(strategyConfig); //开始执行代码生成 autoGenerator.execute(); }}

主键自增

数据库明明设置了主键自增,为什么添加数据的时候却给了一串很长的随机数呢?

其实这是mybatis-plus帮我们生成的主键id,采用的是twitter的雪花算法,用毫秒数、机器编号等参数形成的唯一id

我们想要变成我们想要的1,2,3,4,5这种简单的自增也很简单,添加一个注解就好了

时间戳

阿里巴巴Java开发手册上明确表示了数据库建表的时候必须要有创建时间(gmt_create)、修改时间(gmt_modified)字段

我们来实际使用一下

  • 数据库建表加上这两个字段,并且类型设置timestamp

    • 创建时间

      • 默认时现在操作的时间,并且不再更新
    • 修改时间

      • 默认是现在的操作时间,并且会因为数据更新而更新时间戳

上面的操作在数据库中操作以及完全可以玩的来了,但是实际开发我却发现更新不生效

所以我查了很多资料,发现解决方式有两种,一种是加注解,一种是加注解和配置类

既然一个注解可以解决的事情,我为什么还要加配置类呢?完事收工

逻辑删除

之前写逻辑删除的时候每次写sql都要手动拼接is_delete = 0,现在mybatis-plus帮我们封装好了

逻辑删除字段添加注解

  • @TableLogic    private Integer isDelete;     
    

配置拦截器

  • package com.xiaoye.config;import com.baomidou.mybatisplus.core.injector.ISqlInjector;import com.baomidou.mybatisplus.extension.injector.LogicSqlInjector;import com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor;import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;import org.mybatis.spring.annotation.MapperScan;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;/** * @author 小也 * @create 2021/4/22 16:40 /@MapperScan("com.xiaoye.mapper")@Configurationpublic class MybatisPlusConfig { /* * 乐观锁 * @return / @Bean public OptimisticLockerInterceptor optimisticLockerInterceptor(){ return new OptimisticLockerInterceptor(); } /* * 分页组件 * @return / @Bean public PaginationInterceptor paginationInterceptor(){return new PaginationInterceptor();} /* * 逻辑删除 * @return */ @Bean public ISqlInjector iSqlInjector(){return new LogicSqlInjector();}}

application.properties配置文件里配置逻辑删除的代号

  • 开启日志输出mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl#逻辑删除,未删除是0,已删除是1mybatis-plus.global-config.db-config.logic-not-delete-value=0mybatis-plus.global-config.db-config.logic-delete-value=1

开始测试

  • 测试代码

    • @Test    void logicDeleteTest(){        sysUserMapper.deleteById(1);    }
      
  • 测试结果

    • 先执行删除操作
    • 再次查询发现已经查不到了
    • 我们可以通过navicat看到实际上只是更改了is_delete的值,并没有真的删除

性能分析插件

性能分析插件就是记录每条sql执行的时间,超时就会停止运行

导入插件,注册组件

  • @Bean    public PerformanceInterceptor performanceInterceptor(){        PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();        //这里是毫秒数,设置sql执行的最大时间,超时就停止        performanceInterceptor.setMaxTime(100);        //开启sql格式化        performanceInterceptor.setFormat(true);        return performanceInterceptor;    }
    

开始测试

  • 这个很常用,可以有效找到慢查询