MyBatis-Plus笔记

Mybatis-plus学习

添加依赖

<!-- 数据库驱动 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- lombok -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>
<!-- mybatis-plus -->
<!-- mybatis-plus 是自己开发,并非官方的! -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.0.5</version>
</dependency>

配置数据库信息

# 数据库连接配置
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
    username: root
    password: baibing123

配置日志

查看sql执行日志

# 配置日志
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

CRUD扩展

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

雪花算法

snowflake是twitter开源的分布式ID生成算法,结果是一个long型的id

其核心思想是:使用64bit作为毫秒数,10bit作为机器id(5个bit是数据中心,5个bit是机器ID) 12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生4096个ID) 最后一个是符号位 永远是0.可以保证几乎全球唯一

INSERT

@Test
public void TestInsert(){
    User user = new User();
    user.setName("Jhonny");
    user.setAge(3);
    user.setEmail("123@qq.com");
    int result = userMapper.insert(user);
    System.out.println(result);
}

UPDATE

所有的sql都是自动动态配置

自动填充

创建时间,修改时间,这个操作一遍都是自动化完成的。

  1. 删除数据库的默认值,更新操作(修改时间)

  2. 实体类字段属性上需要添加注解

    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
    
  3. 编写处理器来处理这个注解
    @Slf4j
    @Component
    public class MyMetaObjectHandler implements MetaObjectHandler {
    
       //插入时的填充策略
       @Override
       public void insertFill(MetaObject metaObject) {
           log.info("start insert fill...");
           this.setFieldValByName("createTime",new Date(),metaObject);
           this.setFieldValByName("updateTime",new Date(),metaObject);
       }
       //更新时的填充策略
       @Override
       public void updateFill(MetaObject metaObject) {
           log.info("start update fill...");
           this.setFieldValByName("updateTime",new Date(),metaObject);
       }
    }
    

乐观锁

乐观锁实现方式:

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

MP添加乐观锁

  1. 数据库添加version字段

  2. 实体类加对应字段

    @Version//乐观锁version注解
    private Integer version; 
    
  3. 注册组件
    //扫描mapper文件夹
    @MapperScan("cn.bybing.mapper")
    @EnableTransactionManagement//TX
    @Configuration//配置类
    public class MyBatisPlusConfig {
       //注册乐观锁插件
       @Bean
       public OptimisticLockerInterceptor optimisticLockerInterceptor(){
           return new OptimisticLockerInterceptor();
       }
    }
    
  4. 测试代码
    @Test
    //测试乐观锁成功
    public void testOptimisticLocker(){
       //1.查询用户信息
       User user = userMapper.selectById(1L);
       //2.修改用户信息
       user.setName("jhonny22");
       user.setEmail("jhonny22@163.com");
       //3.执行更新操作
       userMapper.updateById(user);
    }
    
    @Test
    //测试乐观锁失败,多线程下
    public void testOptimisticLocker2(){
       //线程1
       //1.查询用户信息
       User user = userMapper.selectById(1L);
       //2.修改用户信息
       user.setName("jhonny111");
       user.setEmail("jhonny111@163.com");
    
       //模拟另外一个线程执行了插队操作
       //1.查询用户信息
       User user2 = userMapper.selectById(1L);
       //2.修改用户信息
       user2.setName("jhonny222");
       user2.setEmail("jhonny222@163.com");
       //3.执行更新操作
       userMapper.updateById(user2);
    
       userMapper.updateById(user);
    }
    

查询操作

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

//条件查询
@Test
public void testSelectByMap(){
    HashMap<String, Object> map = new HashMap<>();
    map.put("age",24);
    List<User> users = userMapper.selectByMap(map);
    users.forEach(System.out::println);
}

分页查询

  1. 在mp配置类中添加拦截器
    @Bean
    public PaginationInterceptor paginationInterceptor() {
       PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
       // 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求  默认false
       // paginationInterceptor.setOverflow(false);
       // 设置最大单页限制数量,默认 500 条,-1 不受限制
       // paginationInterceptor.setLimit(500);
       // 开启 count 的 join 优化,只针对部分 left join
       paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
       return paginationInterceptor;
    }
    
    可以简写:
       //分页插件
       @Bean
       public PaginationInterceptor paginationInterceptor() {
       return new PaginationInterceptor();
    }
    

    测试:

    //测试分页查询
    @Test
    public void testPage(){
       // 参数1:当前页
       // 参数2:页面大小
       Page<User> page = new Page<>(1,5);
       IPage<User> userIPage = userMapper.selectPage(page, null);
       userIPage.getRecords().forEach(System.out::println);
    

删除操作

类似添加操作

逻辑删除

物理删除:从数据库中直接移除
逻辑删除:在数据库中没有被移除,而是通过一个变量来让它失效,deleted=0 => deleted=1

防止数据的丢失,类似回收站

测试:

  1. 在数据库中添加deleted字段

  2. 实体类中添加属性

    @TableLogic //逻辑删除
    private Integer deleted;
    
  3. 配置
    //逻辑删除组件
    @Bean
    public ISqlInjector sqlInjector(){
       return new LogicSqlInjector();
    }
    

性能分析插件

在mp配置类中添加插件代码

//sql执行效率插件
@Bean
@Profile({"dev","test"})
public PerformanceInterceptor performanceInterceptor(){
    PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
    performanceInterceptor.setMaxTime(100);//设置sql执行最大时间。如果超过了则不执行
    performanceInterceptor.setFormat(true);
    return performanceInterceptor;
}

条件构造器(Wrapper)

demo1:

@Test
void contextLoad(){
    // 查询name不为空的用户,并且邮箱不为空,年龄大于等于12
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.isNotNull("name")
            .isNotNull("email")
            .ge("age",12);
    userMapper.selectList(wrapper).forEach(System.out::println);
}

demo2:

@Test
public void testWrapper(){
    //查询指定名字
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.eq("name", "jhonny222");
    System.out.println(userMapper.selectOne(wrapper));
}

模糊查询

@Test
public void testWrapper3(){
    //查询年龄在20 - 30 岁之间的
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.notLike("name","e")
        // 左和右 likeRight:t% likeLeft:%t
        .likeRight("email","t");
    List<Map<String, Object>> maps = userMapper.selectMaps(wrapper);
    maps.forEach(System.out::println);
}

代码自动生成器

https://baomidou.com/pages/779a6e/

  1. 添加依赖
    <!--mybatis-plus-->
    <dependency>
       <groupId>com.baomidou</groupId>
       <artifactId>mybatis-plus-boot-starter</artifactId>
       <version>3.5.0</version>
    </dependency>
    <!--mp代码生成器-->
    <dependency>
       <groupId>com.baomidou</groupId>
       <artifactId>mybatis-plus-generator</artifactId>
       <version>3.4.1</version>
    </dependency>
    <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-freemarker</artifactId>
    </dependency>
    
  2. CodeGenerator.java
    public class CodeGenerator {
    
       /**
        * <p>
        * 读取控制台内容
        * </p>
        */
       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) {
           // 代码生成器
           AutoGenerator mpg = new AutoGenerator();
    
           // 全局配置
           GlobalConfig gc = new GlobalConfig();
           String projectPath = System.getProperty("user.dir");
           gc.setOutputDir(projectPath + "/src/main/java");
           gc.setAuthor("jhonny");
           gc.setOpen(false);
           // gc.setSwagger2(true); 实体属性 Swagger2 注解
           gc.setEntityName("%s");
           gc.setMapperName("%sMapper");
           gc.setControllerName("%sController");
           gc.setServiceName("%sService");
           gc.setServiceImplName("%sServiceImpl");
           mpg.setGlobalConfig(gc);
    
           // 数据源配置
           DataSourceConfig dsc = new DataSourceConfig();
           dsc.setUrl("jdbc:mysql://localhost:3306/yiqing?useUnicode=true&useSSL=false&characterEncoding=utf8");
           // dsc.setSchemaName("public");
           dsc.setDriverName("com.mysql.cj.jdbc.Driver");
           dsc.setUsername("root");
           dsc.setPassword("baibing123");
           mpg.setDataSource(dsc);
    
           // 包配置
           PackageConfig pc = new PackageConfig();
           //        pc.setModuleName(scanner("模块名"));
           pc.setParent("cn.bybing");
           mpg.setPackageInfo(pc);
    
           // 自定义配置
           InjectionConfig cfg = new InjectionConfig() {
               @Override
               public void initMap() {
                   // to do nothing
               }
           };
    
           // 如果模板引擎是 freemarker
           String templatePath = "/templates/mapper.xml.ftl";
           // 如果模板引擎是 velocity
           // String templatePath = "/templates/mapper.xml.vm";
    
           // 自定义输出配置
           List<FileOutConfig> 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.setFileCreate(new IFileCreate() {
               @Override
               public boolean isCreate(ConfigBuilder configBuilder, FileType fileType, String filePath) {
                   // 判断自定义文件夹是否需要创建
                   checkDir("调用默认方法创建的目录,自定义目录用");
                   if (fileType == FileType.MAPPER) {
                       // 已经生成 mapper 文件判断存在,不想重新生成返回 false
                       return !new File(filePath).exists();
                   }
                   // 允许生成模板文件
                   return true;
               }
           });
           */
           cfg.setFileOutConfigList(focList);
           mpg.setCfg(cfg);
    
           // 配置模板
           TemplateConfig templateConfig = new TemplateConfig();
    
           // 配置自定义输出模板
           //指定自定义模板路径,注意不要带上.ftl/.vm, 会根据使用的模板引擎自动识别
           // templateConfig.setEntity("templates/entity2.java");
           // templateConfig.setService();
           // templateConfig.setController();
    
           templateConfig.setXml(null);
           mpg.setTemplate(templateConfig);
    
           // 策略配置
           StrategyConfig strategy = new StrategyConfig();
           strategy.setNaming(NamingStrategy.underline_to_camel);
           strategy.setColumnNaming(NamingStrategy.underline_to_camel);
           strategy.setSuperEntityClass("");
           strategy.setEntityLombokModel(true);
           strategy.setRestControllerStyle(true);
           // 公共父类
           strategy.setSuperControllerClass("");
           // 写于父类中的公共字段
           strategy.setSuperEntityColumns("id");
           strategy.setInclude(scanner("表名,多个英文逗号分割").split(","));
           strategy.setControllerMappingHyphenStyle(true);
           strategy.setTablePrefix(pc.getModuleName() + "_");
           mpg.setStrategy(strategy);
           mpg.setTemplateEngine(new FreemarkerTemplateEngine());
           mpg.execute();
       }
    }
    

留下评论

您的电子邮箱地址不会被公开。 必填项已用*标注