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>
    
  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");
    

    阿里的开发手册规定: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 
    
  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(); // 执行
        }
    

    运行后,就会发现:

    在这里插入图片描述

    直接爽。

三、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);
    }
}

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

测试:

@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);
}

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

3.2 读取查询(Retrieve)

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

@Test
void selectTest() {
    // 查询所有
    userService.list().forEach(System.out::println);
}

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

在这里插入图片描述

如果要条件查询,就需要学习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);
}

在这里插入图片描述

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

3.4 和删除(Delete)

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

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

@Test
void deleteTest() {
    User user = new User();
    user.setDsUserId(9);
    userService.removeById(user);
}

这里将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);
}

生成了这样一个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);
}

生成的sql:

在这里插入图片描述

五、插件

内置了多个插件

在这里插入图片描述

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

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

首先添加配置类:

@Configuration
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.POSTGRE_SQL));
        return interceptor;
    }
}

然后就能直接用了:

@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());
}

总结:一个字,爽!

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐