• MyBatis-plus


    本篇基于MP 3.3.0 版本 ,spring boot 方式。

    MyBatis-plus 简介:

      MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 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 操作智能分析阻断,也可自定义拦截规则,预防误操作

      支持数据库

    • mysql 、 mariadb 、 oracle 、 db2 、 h2 、 hsql 、 sqlite 、 postgresql 、 sqlserver

    • 达梦数据库 、 虚谷数据库 、 人大金仓数据库

      快速开始

    1. 建立数据库,导入数据
    2. 导入对应的依赖
    3. 编写项目
      1. 配置数据库连接
      2. 编写实体类
      3. 编写Mapper接口继承BaseMapper<T> 即可!至此所有CRUD都已完成
    4. 测试使用

      详细看官网:https://mybatis.plus

    插入策略

    • 用户ID自动回填
    • 主键生成策略
      • 自增
      • 手动输入
      • UUID
      • 雪花算法
      • 唯一ID生成器
      • 唯一ID生成器(字符串类型)
    • 关于id生成的一些策略请参看:https://www.cnblogs.com/haoxinyue/p/5208136.html

     1 public enum IdType {
     2    // 自增 
     3    AUTO(0),
     4    NONE(1),
     5    // 手动输入 
     6    INPUT(2), 
     7    // 唯一id生成器,默认策略
     8    ID_WORKER(3),
     9    // uuid
    10    UUID(4), 
    11    // 唯一id生成器(字符串类型)
    12    ID_WORKER_STR(5); }

    更新策略

      阿里巴巴规范中,id、gmt_create、gmr_update !

     给数据库表增加两个字段测试

    1. 自动更新策略两种级别
      1. 数据库级别(不建议)
        1. 设置默认值
        2. 插入、更新测试
      2. 代码级别(建议)
        1. 删除更新默认值
        2. 编写实体类对应注解
        3. 编写注解的处理器

      数据库级别

      代码级别

    1     @TableField(fill = FieldFill.INSERT)
    2     private Date insertTime; // 创建时间
    3     @TableField(fill = FieldFill.INSERT_UPDATE)
    4     private Date updateTime; // 更新时间
     1 package com.coding.handler;
     2 
     3 import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
     4 import org.apache.ibatis.reflection.MetaObject;
     5 import org.springframework.stereotype.Component;
     6 
     7 import java.util.Date;
     8 
     9 @Component
    10 public class MyMetaObjectHandler implements MetaObjectHandler {
    11     /*
    12     * default MetaObjectHandler setFieldValByName(String fieldName, Object fieldVal, MetaObject metaObject) {
    13         if (Objects.nonNull(fieldVal) && metaObject.hasSetter(fieldName)) {
    14             metaObject.setValue(fieldName, fieldVal);
    15         }
    16         return this;
    17     }*/
    18     @Override
    19     public void insertFill(MetaObject metaObject) {
    20         this.setFieldValByName("insertTime",new Date(),metaObject);
    21         this.setFieldValByName("updateTime",new Date(),metaObject);
    22     }
    23 
    24     @Override
    25     public void updateFill(MetaObject metaObject) {
    26         this.setFieldValByName("updateTime",new Date(),metaObject);
    27     }
    28 }

    乐观锁

    • 乐观锁:非常乐观,无论什么操作都不加锁!(思考:分布式环境下怎么解决冲突问题?)
    • 悲观锁:非常悲观,无论什么操作都加锁!默认情况下都是悲观锁!

    解决问题

      通常方式就是增加一个乐观锁字段(version)

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

    乐观锁实现方式:

    1. 取出记录时,获取当前version (查询)old version = 1
    2. 更新时,带上这个version (更新)
    3. 执行更新时,set version = newVersion where version = old version
      1. 如果version不对应,就更新失败
      2. 对应,就更新成功

    MP对乐观锁也进行了支持

    1. 添加version注解到字段上面
    2. 添加乐观锁插件即可
    3. 测试就自动带上了版本号

      详细见:https://mp.baomidou.com/guide/optimistic-locker-plugin.html#%E4%B8%BB%E8%A6%81%E9%80%82%E7%94%A8%E5%9C%BA%E6%99%AF

    查询策略

      注:分页查询需要添加以下插件:

     1 //Spring boot方式
     2 @EnableTransactionManagement
     3 @Configuration
     4 @MapperScan("com.baomidou.cloud.service.*.mapper*")
     5 public class MybatisPlusConfig {
     6 
     7     @Bean
     8     public PaginationInterceptor paginationInterceptor() {
     9         PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
    10         // 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求  默认false
    11         // paginationInterceptor.setOverflow(false);
    12         // 设置最大单页限制数量,默认 500 条,-1 不受限制
    13         // paginationInterceptor.setLimit(500);
    14         // 开启 count 的 join 优化,只针对部分 left join
    15         paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
    16         return paginationInterceptor;
    17     }
    18 }
     1     // 查询单个
     2     @Test
     3     void testSelectById(){
     4         User user = userMapper.selectById(1L);
     5         System.out.println(user);
     6     }
     7 
     8     // 查询多个
     9     @Test
    10     void testSelectBatchIds(){
    11         List<User> users = userMapper.selectBatchIds(Arrays.asList(1L, 2L, 3L));
    12         users.forEach(System.out::println);
    13     }
    14 
    15     // 查询数量
    16     @Test
    17     void testSelectCount(){
    18         Integer count = userMapper.selectCount(null);
    19         System.out.println(count);
    20     }
    21 
    22     // 简单条件查询
    23     @Test
    24     void testSelectByMap(){
    25         Map<String, Object> map = new HashMap<>();
    26         map.put("name","zhangsan");
    27         map.put("age",18);
    28         List<User> users = userMapper.selectByMap(map);
    29         users.forEach(System.out::println);
    30     }
    31 
    32     // 分页查询
    33     @Test
    34     void testSelectPage(){
    35         Page<User> page = new Page<>();
    36         Page<User> userPage = userMapper.selectPage(page, null);
    37         List<User> users = userPage.getRecords();
    38         users.forEach(System.out::println);
    39         System.out.println(userPage.getSize());
    40         System.out.println(userPage.getTotal());
    41         System.out.println(userPage.getPages());
    42     }

    删除策略

     1     // 删除一个
     2     @Test
     3     void testDeleteById(){
     4         userMapper.deleteById(1L);
     5     }
     6 
     7     // 删除多个
     8     @Test
     9     void testDeleteBatchIds(){
    10         userMapper.deleteBatchIds(Arrays.asList(2L,6L));
    11     }
    12 
    13     // 根据条件删除
    14     @Test
    15     void testDeleteByMap(){
    16         HashMap<String, Object> map = new HashMap<>();
    17         map.put("name","Tom");
    18         userMapper.deleteByMap(map);
    19     }

    拓展

    • 逻辑删除:并不是真的从数据库中删掉了,只是增加了一个判断而已
      • 业务场景:管理员在后台可以看到被删除的记录,使用逻辑删除可以保证项目的安全和健壮性
    • 物理删除:直接从数据库中删除

    逻辑删除使用测试

    1. 添加逻辑删除字段
    2. 在实体类添加注解
    3. 在配置文件进行配置
    4. 注册Bean

    1     @TableLogic  // 逻辑删除字段注解
    2     private Integer deleted;
    1 # application.yml 加入配置(如果你的默认值和mp默认的一样,该配置可无)
    2 mybatis-plus:
    3   global-config:
    4     db-config:
    5       logic-delete-field: flag  #全局逻辑删除字段值 3.3.0开始支持,详情看下面。
    6       logic-delete-value: 1 # 逻辑已删除值(默认为 1)
    7       logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
     1 // 注册 Bean(3.1.1开始不再需要这一步)
     2 
     3 import com.baomidou.mybatisplus.core.injector.ISqlInjector;
     4 import com.baomidou.mybatisplus.extension.injector.LogicSqlInjector;
     5 import org.springframework.context.annotation.Bean;
     6 import org.springframework.context.annotation.Configuration;
     7 
     8 @Configuration
     9 public class MyBatisPlusConfiguration {
    10 
    11     @Bean
    12     public ISqlInjector sqlInjector() {
    13         return new LogicSqlInjector();
    14     }
    15 }
    1     // 逻辑删除,删除一个
    2     @Test
    3     void testLogicDeleteById(){
    4         userMapper.deleteById(1L);
    5     }

    性能分析

      在开发的时候排除慢sql! 

      3.2.0版本以上参考:https://mp.baomidou.com/guide/p6spy.html

    1. 导入p6spy的maven依赖
    2. 在dev环境下配置文件进行配置
    3. spy.properties配置

      3.2.0版本以下参考:https://mp.baomidou.com/guide/performance-analysis-plugin.html

    条件构造器

      我们平时写的sql,一般使用的最多的是一些条件,这些东西MP也帮助我们提供了

      更多条件查询见官网:https://mp.baomidou.com/guide/wrapper.html#abstractwrapper

     1 package com.coding;
     2 
     3 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
     4 import com.coding.mapper.UserMapper;
     5 import com.coding.pojo.User;
     6 import org.junit.jupiter.api.Test;
     7 import org.springframework.beans.factory.annotation.Autowired;
     8 import org.springframework.boot.test.context.SpringBootTest;
     9 
    10 @SpringBootTest
    11 public class WrapperTest {
    12     @Autowired
    13     private UserMapper userMapper;
    14   // 仅举一例,更多条件查询见官网
    15     @Test
    16     void testConditionSelect(){
    17         QueryWrapper<User> wrapper = new QueryWrapper<>();
    18         wrapper.eq("name","zhangsan") // 姓名 = zhangsan
    19                 .gt("age",18); // 年龄 > 18
    20         userMapper.selectList(wrapper);
    21     }
    22 }

    MP 一键生成 ——非常便捷

      导入依赖

     1         <!-- MP -->
     2         <dependency>
     3             <groupId>com.baomidou</groupId>
     4             <artifactId>mybatis-plus-boot-starter</artifactId>
     5         </dependency>
     6         <!-- velocity 模板引擎, Mybatis Plus 代码生成器需要 -->
     7         <dependency>
     8             <groupId>org.apache.velocity</groupId>
     9             <artifactId>velocity-engine-core</artifactId>
    10         </dependency>    
     1 package com.coding.utils;
     2 
     3 import com.baomidou.mybatisplus.annotation.DbType;
     4 import com.baomidou.mybatisplus.annotation.FieldFill;
     5 import com.baomidou.mybatisplus.annotation.IdType;
     6 import com.baomidou.mybatisplus.generator.AutoGenerator;
     7 import com.baomidou.mybatisplus.generator.config.*;
     8 import com.baomidou.mybatisplus.generator.config.po.TableFill;
     9 import com.baomidou.mybatisplus.generator.config.rules.DateType;
    10 import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
    11 
    12 import java.util.ArrayList;
    13 import java.util.List;
    14 
    15 // 遇事不决就重启,遇事不决就重启,遇事不决就重启 
    16 // 自动生成代码
    17 public class CodeGenerator {
    18 
    19     public static void main(String[] args) {
    20 
    21         String moduleName = "edu";
    22 
    23         // 1.代码生成器
    24         AutoGenerator mpg = new AutoGenerator();
    25 
    26         // 规则的配置
    27         // 全局配置
    28         GlobalConfig gc = new GlobalConfig();
    29         // 获取当前项目路径
    30         String projectPath = System.getProperty("user.dir");
    31         gc.setOutputDir(projectPath + "/coding-edu-edu/src/main/java");
    32         // 全局自动添加作者
    33         gc.setAuthor("coding");
    34         // 配置是否开启文件夹弹出
    35         gc.setOpen(false);
    36         // 配置是否覆盖之前生成的文件夹
    37         gc.setFileOverride(false);
    38         // 配置去掉service接口命名的“I” ,例:IUserService 变成UserService
    39         gc.setServiceName("%sService");
    40         // 配置主键策略
    41         gc.setIdType(IdType.ID_WORKER_STR);
    42         // 配置日期相关的 ,日期类型
    43         gc.setDateType(DateType.ONLY_DATE);
    44         // 开启swagger注解自动生成
    45         gc.setSwagger2(true);
    46         mpg.setGlobalConfig(gc);
    47 
    48         // 数据源配置
    49         DataSourceConfig dsc = new DataSourceConfig();
    50         dsc.setDriverName("com.mysql.cj.jdbc.Driver");
    51         dsc.setUrl("jdbc:mysql://localhost:3306/coding_" + moduleName + "?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC");
    52         dsc.setUsername("root");
    53         dsc.setPassword("root");
    54         dsc.setDbType(DbType.MYSQL);
    55         mpg.setDataSource(dsc);
    56 
    57 
    58         // 包配置
    59         PackageConfig pc = new PackageConfig();
    60         pc.setModuleName(moduleName);
    61         pc.setParent("com.coding");// 父路径
    62         pc.setEntity("entity");
    63         pc.setController("controller");
    64         pc.setService("service");
    65         pc.setMapper("mapper");
    66         mpg.setPackageInfo(pc);
    67 
    68         // 策略配置
    69         StrategyConfig strategy = new StrategyConfig();
    70         // 要生成哪一个表对应的类
    71         strategy.setInclude(moduleName + "_\w*"); // 重要!!!
    72         strategy.setTablePrefix(pc.getModuleName() + "_"); // edu_ 这个前缀不生成在类名中 例:edu_name --> Name
    73         // 数据库表生成到实体类 下划线转驼峰
    74         strategy.setNaming(NamingStrategy.underline_to_camel);
    75         // 自动生成lombok注解
    76         strategy.setEntityLombokModel(true);
    77         // 配置逻辑删除字段
    78         strategy.setLogicDeleteFieldName("is_deleted");
    79         strategy.setEntityBooleanColumnRemoveIsPrefix(true);
    80         // 自动填充
    81         TableFill gmt_create = new TableFill("gmt_create", FieldFill.INSERT);
    82         TableFill gmt_modified = new TableFill("gmt_modified", FieldFill.INSERT_UPDATE);
    83         List<TableFill> list = new ArrayList<>();
    84         list.add(gmt_create);
    85         list.add(gmt_modified);
    86         strategy.setTableFillList(list);
    87         // 乐观锁
    88         strategy.setVersionFieldName("version");
    89         // restful api
    90         strategy.setRestControllerStyle(true);
    91         //
    92         strategy.setControllerMappingHyphenStyle(true); // 请求示例:/user/hello_name 而不是/user/helloName
    93 
    94         mpg.setStrategy(strategy);
    95         // 2.执行生成器
    96         mpg.execute();
    97     }
    98 
    99 }

    运行后的文件结构截图

      部分源码截图

      entity

      mapper

      mapper.xml

       service 接口

      service 实现类

      controller

  • 相关阅读:
    网络爬虫之Cookies解决
    高性能异步爬虫
    Python csv存储
    pandas ExcelWriter用法及代码示例
    pandas to_excel 用法详解
    pandas read_csv 与 to_csv 方法各参数详解
    pandas read_excel操作
    pandas DataFrame.groupby()所见的各种用法详解
    Pandas 中 DataFrame 基本函数整理
    Python 解析二维码 输出文本
  • 原文地址:https://www.cnblogs.com/ShallowPen/p/12651039.html
Copyright © 2020-2023  润新知