• Mybatis-Plus入门(新版3.5.2)


    Mybatis-Plus入门(新版3.5.2)


    环境:

    jdk:1.8

    mybatis-plus:3.5.2

    springboot:2.7.1

    PostgreSQL:14.4

    mybatis-plus官网

    文档说明:本文的会直接从代码生成开始,用最短的时间,最少的弯路学会使用mybatis-plus,提高开发效率,但是需要一定的基础。

    一、概述

    不多说生涩的理论,我的感受是:

    • 不用再写简单的SQL语句,单表CRUD直接就有了。
    • 自带分页插件,好用的一批。
    • 代码生成器,开发效率无敌。

    虽然复杂的SQL还是要手写,但是,都这样了还要啥自行车?

    二、代码生成

    1. 新建springboot项目,添加依赖

      <!--数据库驱动-->
      <dependency>
          <groupId>org.postgresql</groupId>
          <artifactId>postgresql</artifactId>
      </dependency>
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-jdbc</artifactId>
      </dependency>
      <!--数据库连接池-->
      <dependency>
          <groupId>com.alibaba</groupId>
          <artifactId>druid-spring-boot-starter</artifactId>
          <version>1.2.11</version>
      </dependency>
      <dependency>
          <groupId>com.baomidou</groupId>
          <artifactId>mybatis-plus-boot-starter</artifactId>
          <version>3.5.2</version>
      </dependency>
      <dependency>
          <groupId>org.projectlombok</groupId>
          <artifactId>lombok</artifactId>
      </dependency>
      <!--代码生成器-->
      <dependency>
          <groupId>com.baomidou</groupId>
          <artifactId>mybatis-plus-generator</artifactId>
          <version>3.5.2</version>
      </dependency>
      <!--自动代码生成到时候需要配置开启swagger注解,所以导入swagger-->
      <dependency>
          <groupId>io.springfox</groupId>
          <artifactId>springfox-swagger2</artifactId>
          <version>2.9.2</version>
      </dependency>
      <dependency>
          <groupId>io.springfox</groupId>
          <artifactId>springfox-swagger-ui</artifactId>
          <version>2.9.2</version>
      </dependency>
      <!--模板引擎,规定必须设置-->
      <dependency>
          <groupId>org.apache.velocity</groupId>
          <artifactId>velocity-engine-core</artifactId>
          <version>2.3</version>
      </dependency>
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
    2. 配置数据源

      代码生成是根据数据库生成相应的实体,服务等,所以我们要先建一张表:

      /*
       Navicat Premium Data Transfer
      
       Source Server         : localhost_5432
       Source Server Type    : PostgreSQL
       Source Server Version : 140004
       Source Host           : localhost:5432
       Source Catalog        : db_ds_ss
       Source Schema         : test
      
       Target Server Type    : PostgreSQL
       Target Server Version : 140004
       File Encoding         : 65001
      
       Date: 01/07/2022 11:39:21
      */
      
      
      -- ----------------------------
      -- Table structure for ds_user
      -- ----------------------------
      DROP TABLE IF EXISTS "test"."ds_user";
      CREATE TABLE "test"."ds_user" (
        "ds_user_name" varchar(255) COLLATE "pg_catalog"."default" NOT NULL,
        "ds_user_password" varchar(255) COLLATE "pg_catalog"."default" NOT NULL,
        "ds_user_email" varchar(255) COLLATE "pg_catalog"."default",
        "ds_user_tel" varchar(255) COLLATE "pg_catalog"."default",
        "ds_user_role" int2 NOT NULL,
        "create_time" timestamp(0) DEFAULT now(),
        "modify_time" timestamp(0) DEFAULT now(),
        "deleted" int2 NOT NULL DEFAULT 0,
        "ds_user_id" serial4 NOT NULL
      )
      ;
      COMMENT ON COLUMN "test"."ds_user"."ds_user_name" IS '用户名  ';
      COMMENT ON COLUMN "test"."ds_user"."ds_user_password" IS '密码';
      COMMENT ON COLUMN "test"."ds_user"."ds_user_email" IS '邮箱';
      COMMENT ON COLUMN "test"."ds_user"."ds_user_tel" IS '手机';
      COMMENT ON COLUMN "test"."ds_user"."ds_user_role" IS '用户权限:如果为0则为普通用户,1则为管理员  ';
      COMMENT ON COLUMN "test"."ds_user"."create_time" IS '创建时间 ';
      COMMENT ON COLUMN "test"."ds_user"."modify_time" IS '修改时间  ';
      COMMENT ON COLUMN "test"."ds_user"."deleted" IS '删除标志 ,逻辑删除,0表示未删除,1表示删除';
      COMMENT ON COLUMN "test"."ds_user"."ds_user_id" IS 'id ,自增';
      
      -- ----------------------------
      -- Uniques structure for table ds_user
      -- ----------------------------
      ALTER TABLE "test"."ds_user" ADD CONSTRAINT "ds_user_ds_user_id_key" UNIQUE ("ds_user_name");
      
      -- ----------------------------
      -- Primary Key structure for table ds_user
      -- ----------------------------
      ALTER TABLE "test"."ds_user" ADD CONSTRAINT "ds_user_pk" PRIMARY KEY ("ds_user_id");
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53

      阿里的开发手册规定:id, gmt_create, gmt_modified必须要有。(创建、修改时间自动插入实现见下文)

      另外,逻辑删除也可以加上,也就是用一个表示判断数据是否被删除了,而不是真正的移除这一条数据,删除的sql也从delete变成了update xxx set delete=1 where xxx

      yml中配置数据源:

      spring:
        datasource:
          druid:
            driver-class-name: org.postgresql.Driver
            url: jdbc:postgresql://localhost:5432/db_ds_ss?currentSchema=test
            username: postgres
            password: 123456
            initial-size: 5
            max-active: 20
            min-idle: 5
            max-wait: 60000
      mybatis-plus:
        configuration:
        #日志输出
          log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
        global-config:
          db-config:
          #逻辑删除
            logic-delete-value: 1
            logic-not-delete-value: 0 
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
    3. 写代码

      搞个测试就可以了,方便

          @Test
          void autocode() {
              // 最开始是配置数据源,postgresql还得指定架构,这里我就指定了用test架构
              FastAutoGenerator.create(new DataSourceConfig.Builder("jdbc:postgresql://localhost:5432/db_ds_ss?currentSchema=test", "postgres", "123456").schema("test"))
                      // 全局配置
                      .globalConfig(builder -> {
                          builder.author("yangsf") // 设置作者
                                  .enableSwagger() // 开启 swagger 模式(根据数据库的注释等生成swagger的注解,真的牛)
                                  .fileOverride() // 覆盖已生成文件
                                  .outputDir(System.getProperty("user.dir") + "/src/main/java") // 指定输出目录
                                  .disableOpenDir() // 生成后不打开资源管理器
                                  .dateType(DateType.ONLY_DATE) // 时间类型
                                  .commentDate("yyyy-MM-dd hh:mm:ss"); // 时间格式
                      })
                      // 包配置,也就是生成哪些文件夹
                      .packageConfig(builder -> {
                          builder.parent("com.yangsf") // 设置父包名 也就是生成在输出目录的 com/yangsf下
                                  .moduleName("mybatisplus") // 设置父包模块名 也就是代码放在com/yangsf/mybatisplus下
                                  .pathInfo(Collections.singletonMap(OutputFile.xml, System.getProperty("user.dir")+"/src/main/resources/mapper")) // 设置mapperXml生成路径
                                  .service("service") // 接下来的几个都是包名,也就是对应的代码的存放目录
                                  .entity("entity")
                                  .mapper("mapper")
                                  .controller("controller")
                                  .xml("mapper")
                                  .other("utils");
                      })
                      // 策略配置
                      .strategyConfig(builder -> {
                          builder.addInclude("ds_user") // 这里填数据库表名,可以填多个,用’,‘隔开
                                  .addTablePrefix("ds_") // 忽略前缀
                                  // mapper(dao)层的配置
                                  .mapperBuilder()
                                  .formatMapperFileName("%sMapper") // 生成的文件名都叫xxxmapper(根据表名)
                                  .enableMapperAnnotation()   //开启@mapper注解
                                  .superClass(BaseMapper.class) // 继承BaseMapper类
                                  .formatXmlFileName("%sMapper")   // 生成对应的xml都叫xxxmapper
                                  // service层配置
                                  .serviceBuilder()
                                  .formatServiceFileName("%sService")
                                  .formatServiceImplFileName("%sServiceImpl")
                                  // 实体类配置
                                  .entityBuilder()
                                  .idType(IdType.AUTO) // 主键的策略,我选的是自增
                                  .enableLombok() //开启 Lombok
                                  .disableSerialVersionUID()  //不实现 Serializable 接口,不生产 SerialVersionUID
                                  .logicDeleteColumnName("deleted")   //逻辑删除字段名
                                  .naming(NamingStrategy.underline_to_camel)  //数据库表映射到实体的命名策略:下划线转驼峰命
                                  .columnNaming(NamingStrategy.underline_to_camel)    //数据库表字段映射到实体的命名策略:下划线转驼峰命
                                  .addTableFills(
                                          new Column("create_time", FieldFill.INSERT),
                                          new Column("modify_time", FieldFill.INSERT_UPDATE)
                                  )   //添加表字段填充,"create_time"字段自动填充为插入时间,"modify_time"字段自动填充为插入修改时间,到时候需要编写具体的代码实现
                                  .enableTableFieldAnnotation()     // 开启生成字段注解
                                  // controller层配置
                                  .controllerBuilder()
                                  .formatFileName("%sController") //格式化 Controller 类文件名称,%s进行匹配表名,如 UserController
                                  .enableRestStyle();  //开启生成 @RestController 控制器
                      })
                      // 模板引擎配置 ,默认是Velocity
                      .templateEngine(new FreemarkerTemplateEngine())
                      .execute(); // 执行
          }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
      • 58
      • 59
      • 60
      • 61
      • 62

      运行后,就会发现:

      在这里插入图片描述

      直接爽。

    三、CRUD

    代码生成了,怎么用?直接调方法就完事。

    3.1 增加(Create)

    也就是插入。

    这里要注意的是,插入时间也就是creat_time字段,自己每次手动写插入时间是很low比的行为,我们直接统一解决,让他自己插入。

    顺便把修改时间一起搞了。

    首先,要有一个注解@TableField,因我我们生成代码的时候配置了这个注解,所以自己就有了:

    在这里插入图片描述

    CTRL+鼠标左键可以点进FieldFill查看其中的枚举的解释。

    我们需要一个策略来填充这两个字段,新建一个handler包,新建一个MyMetaObjectHandler类实现MetaObjectHandler接口:

    @Slf4j
    @Component
    public class MyMetaObjectHandler implements MetaObjectHandler {
    
        @Override
        public void insertFill(MetaObject metaObject) {
            log.info("start insert fill ....");
            this.setFieldValByName("createTime", new Date(), metaObject);
        }
    
        @Override
        public void updateFill(MetaObject metaObject) {
            log.info("start update fill ....");
            this.setFieldValByName("modifyTime", new Date(), metaObject);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    我们之后就不用手动输入这两个时间了。

    测试:

    @Autowired
    UserService userService;
    @Test
    void contextLoads() {
        // 保存一个
        User user = new User();
        user.setDsUserName("root");
        user.setDsUserPassword("123456");
        user.setDsUserRole(1);
        userService.save(user);
    
        // 保存多个
        User user1 = new User();
        user1.setDsUserName("admin");
        user1.setDsUserPassword("123456");
        user1.setDsUserRole(1);
        User user2 = new User();
        user2.setDsUserName("xiaoming");
        user2.setDsUserPassword("123456");
        user2.setDsUserRole(1);
        ArrayList<User> users = new ArrayList<>();
        users.add(user1);
        users.add(user2);
        userService.saveBatch(users);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    数据库中果然增加了,并且插入时间也自动填充上了。

    3.2 读取查询(Retrieve)

    先来个简单的查询所有,为了方便我们去实体类中加一个toString方法,然后就可以直接开始查询刚刚插入的数据了:

    @Test
    void selectTest() {
        // 查询所有
        userService.list().forEach(System.out::println);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    日志输出和结果都在这里,注意看标红的地方,这就是利用逻辑删除标志来判断记录是否已被删除。

    在这里插入图片描述

    如果要条件查询,就需要学习Wapper,mybatis-plus的条件构造器,不过我们在这里先不慌学,在之后专门一章来说Wrrapper。

    3.3 更新(Update)

    先来个根据id修改:

    @Test
    void  updateTest() {
        User user = new User();
        user.setDsUserId(9);
        user.setDsUserName("yangsf");r
        userService.updateById(user);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述

    这里将id为9的记录的ds_user_name字段修改为了“yangsf”,并且自动填充了修改时间,复杂的修改需要使用Wrapper(见下文)。

    3.4 和删除(Delete)

    删除如果配置了逻辑删除,那就是逻辑删除(修改删除标志的值),如果没有配置,就是普通删除(删除记录)。

    看个简单的,根据id删除:

    @Test
    void deleteTest() {
        User user = new User();
        user.setDsUserId(9);
        userService.removeById(user);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    这里将id为9的记录逻辑删除了,来看看sql:

    在这里插入图片描述

    实际上只是更改了一个值,并不是物理删除。

    四、Wrapper

    条件构造器,生成sql里where后面的东西,一般分两种wrapper,查询wrapper和更新wrapper(它们俩的父wrapper是AbstractWrapper),能构造的条件很多,不用记,需要的时候去官网看即可,这里说一下用法

    在这里插入图片描述

    举两个例子,一个查一个改,主要是学会怎么看官方文档。

    例1:

    查询权限为1且名为root且id大于3的用户的创建时间

    第一步:查看官方文档构造条件相等的方法

    第二步:查看条件构造大于的方法

    第三步:查看只查询某字段的方法

    于是找到eq,gt和select

    然后看示例,例如eq:

    在这里插入图片描述

    即可写出下面的代码:

    @Test
    void wrapperOne() {
        // 查询权限为1且名为root且id大于3的用户的创建时间
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("ds_user_role", 1)
                .eq("ds_user_name", "root")
                .gt("ds_user_id", 3)
                .select("create_time");
        userService.list(wrapper).forEach(System.out::println);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    生成了这样一个sql:

    在这里插入图片描述

    例2:

    修改名为admin且tel为空的字段的权限为0

    第一步:依然是查看条件构造的方法

    第二部:查看修改的方法

    条件构造不必在说

    于是找到set:

    在这里插入图片描述

    即可写出如下代码:

    @Test
    void wrapperTow() {
        // 修改名为admin且tel为空的字段的权限为0
        UpdateWrapper<User> wrapper = new UpdateWrapper<>();
        wrapper.eq("ds_user_name", "admin")
                .isNull("ds_user_tel")
                .set("ds_user_role", 0);
        userService.update(wrapper);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    生成的sql:

    在这里插入图片描述

    五、插件

    内置了多个插件

    在这里插入图片描述

    这里只说分页,分页是真的爽。

    不用分页插件分页,用limit的话,还要需要计算一下是从多少条开始,使用分页插件就再也不用计算了。

    首先添加配置类:

    @Configuration
    public class MybatisPlusConfig {
        @Bean
        public MybatisPlusInterceptor mybatisPlusInterceptor() {
            MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
            interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.POSTGRE_SQL));
            return interceptor;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    然后就能直接用了:

    @Test
    void pageTest01() {
        // new 一个Page对象即可,第一个参数是页码,第二个参数是页面大小,比如第二页就是2,5
        Page<User> userPage = new Page<>(1, 2);
        // 还可以传个wrapper进去
        userService.page(userPage, null);
        // 输出查询结果
        userPage.getRecords().forEach(System.out::println);
        // 总共的记录数
        System.out.println(userPage.getTotal());
        // 总共的页数
        System.out.println(userPage.getPages());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    总结:一个字,爽!

  • 相关阅读:
    openGauss学习笔记-229 openGauss性能调优-系统调优-配置Ustore
    软件测试基础理论知识—用例篇
    方法的使用
    性能测试 —— Tomcat监控与调优:Jconsole监控
    SpringSecurity(八)【RememberMe记住我】
    自动化运维机器人(RPA)在银行IT运维领域应用场景分析
    nginx反向代理与负载均衡
    20220823 c++
    软考报名全流程及注意事项
    SpringBoot面试之SpringBoot自动装配原理
  • 原文地址:https://blog.csdn.net/yangsf_/article/details/125582515