【SpringBoot框架篇】14.mybatis-plus实战

1.简介

Mybatis-Plus(简称MP)是一个 Mybatis 的增强工具,在 Mybatis 的基础上只做增强不做改变,为简化开发、提高效率而生。这是官方给的定义,关于mybatis-plus的更多介绍及特性,可以参考mybatis-plus官网。那么它是怎么增强的呢?其实就是它已经封装好了一些crud方法,我们不需要再写xml了,直接调用这些方法就行,就类似于JPA。

官网地址: https://mybatis.plus/guide/

使用版本

  • springboot版本是: 2.3.0.RELEASE
  • mybatis-plus-boot-starter的使用的 3.5.2

2添加入依赖

    <properties>
        <java.version>1.8</java.version>
        <mybatis-plus.version>3.5.2</mybatis-plus.version>
        <mysql.version>8.0.11</mysql.version>
    </properties>
    
     <dependencies> 
       <!--MyBatis-Plus 核心依赖 -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>${mybatis-plus.version}</version>
        </dependency>
		
		<!--如果不需要自动生成代码,下面两个包不需要引入-->
        <!-- 代码生成器  MyBatis-Plus 从 3.0.3 之后移除了代码生成器与模板引擎的默认依赖,需要手动添加相关依赖 -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>${mybatis-plus.version}</version>
        </dependency>
        <!--模板引擎-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
        </dependency>
        
        <!--生成的模板实体类用到了lombok注解-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

3.使用代码生成器生成模板代码

3.1.生成代码需要用的测试数据

drop database if exists  boot_master;
create database boot_master;
use boot_master;
drop table if exists  sys_users;

CREATE TABLE sys_users (
id int  AUTO_INCREMENT,
username varchar(20) COMMENT '用户名',
password varchar(20) COMMENT '密码',
create_time datetime COMMENT '创建时间',
update_time datetime COMMENT '修改时间',
grade   int COMMENT '年纪',
 PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert into sys_users(username, password, create_time,update_time,grade) values ('test1','123456','2020-7-1 0:02','2020-7-1 0:02',7),('test2','123456','2020-7-1 0:02','2020-7-1 0:02',7),
('test3','123456','2020-7-1 0:02','2020-7-1 0:02',7),('test4','123456','2020-7-1 0:02','2020-7-1 0:02',8),('test5','123456','2020-7-1 0:02','2020-7-1 0:02',8),
('test6','123456','2020-7-1 0:02','2020-7-1 0:02',8),('test7','123456','2020-7-1 0:02','2020-7-1 0:02',9),('test8','123456','2020-7-1 0:02','2020-7-1 0:02',9),
('test9','123456','2020-7-1 0:02','2020-7-1 0:02',9),('test10','123456','2020-7-1 0:02','2020-7-1 0:02',9),('test11','123456','2020-7-1 0:02','2020-7-1 0:02',9),
('test12','123456','2020-7-1 0:02','2020-7-1 0:02',9);

3.2.创建模板实体类继承通用父类

通常会把一些通用的字段放入到超类中

  • 使用TableField的FieldFill.INSERT指列在插入数据的时候设置默认值
  • 使用TableField的FieldFill.INSERT_UPDATE指列在插入数据的时候设置默认值并在修改的时候自动修改
@Data
@Data
public class BaseEntity implements Serializable {

    @TableId(type = IdType.AUTO)
    private Integer id;

    @TableField(fill = FieldFill.INSERT)
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;

    @TableField(fill = FieldFill.INSERT_UPDATE)
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date updateTime;

}

3.3.模板生成器代码

public class GeneratorMybatisCode {

    //生成类的作者描述
    private final static String AUTHOR = "Dominick Li";
    //数据库连接信息
    private final static String DATASOURCE_URL = "jdbc:mysql://localhost:3306/boot_master?useSSL=false&serverTimezone=GMT%2b8&characterEncoding=utf8&connectTimeout=10000&socketTimeout=3000&autoReconnect=true";
    //数据库用户名
    private final static String DATASOURCE_USERNAME = "root";
    //数据库密码
    private final static String DATASOURCE_PASSWORD = "123456";
    //项目包名全路径
    private final static String BASE_PACKAGE = "com.ljm.boot.mybatisplus";
    //通用父类的字段,该字段不会出现在生成的类中
    private final static String[] SUERP_COLUMN = new String[]{"id", "create_time", "update_time"};
    //需要指定生成模板的表名称
    private final static String[] GENERATOR_TABLES = new String[]{"sys_users"};

    public static void main(String[] args) {
        //项目根路径
        String rootPath = System.getProperty("user.dir");
        //聚合工程需要写工程的名字,普通工程写 "" 就行
        String parantPath = "/14_mybatis_plus";

        FastAutoGenerator.create(DATASOURCE_URL, DATASOURCE_USERNAME, DATASOURCE_PASSWORD)
                //全局配置
                .globalConfig(builder -> {
                    builder.author(AUTHOR) // 设置作者 baomidou 默认值:作者
                            //.enableSwagger() // 开启 swagger 模式 默认值:false
                            .fileOverride() // 覆盖已生成文件 默认值:false
                            .disableOpenDir()//禁止打开输出目录 默认值:true
                            .commentDate("yyyy-MM-dd")// 注释日期
                            .dateType(DateType.ONLY_DATE)//定义生成的实体类中日期类型 DateType.ONLY_DATE 默认值: DateType.TIME_PACK
                            .outputDir(rootPath + parantPath + "/src/main/java");
                })
                //包命名配置
                .packageConfig(builder -> {
                    builder.parent(BASE_PACKAGE)// 父包模块名 默认值:com.baomidou
//                            .controller("controller")//Controller 包名 默认值:controller
//                            .entity("entity")//Entity 包名 默认值:entity
//                            .service("service")//Service 包名 默认值:service
//                            .mapper("mapper")//Mapper 包名 默认值:mapper
                            .pathInfo(Collections.singletonMap(OutputFile.xml, rootPath + parantPath + "/src/main/resources/mapper")); // 设置mapper.xml存放路径
                })
                //生成策略配置
                .strategyConfig(builder -> {
                    builder.addInclude(GENERATOR_TABLES) // 设置需要生成的表名 可边长参数“user”, “user1”
                            //.addTablePrefix("sys_") // 设置生成的累名称过滤表前缀
                            .serviceBuilder()//service策略配置
                            .formatServiceFileName("%sService")
                            .formatServiceImplFileName("%sServiceImpl")
                            .entityBuilder()// 实体类策略配置
                            .idType(IdType.AUTO)//主键策略  根据表是否有自增自动分配
                            .superClass(BaseEntity.class)
                            .addSuperEntityColumns(SUERP_COLUMN) //设置父类的通用字段
                            .enableLombok() //开启lombok
                            //.logicDeleteColumnName("deleted")// 说明逻辑删除是哪个字段
                            .enableTableFieldAnnotation()// 属性加上注解说明
                            .controllerBuilder() //controller 策略配置
                            .formatFileName("%sController")
                            .enableRestStyle() // 开启RestController注解
                            .mapperBuilder()// mapper策略配置
                            .formatMapperFileName("%sMapper")
                            .enableMapperAnnotation()//@mapper注解开启
                            .formatXmlFileName("%sMapper");
                })
                //生成的模板配置 如果用默认的模板则下面的不需要配置
                .templateConfig(
                        builder -> {
                            builder.controller("/template/controller.java");
                            builder.entity("/template/entity.java");
                            builder.mapper("/template/mapper.java");
                            builder.xml("/template/mapper.xml");
                            builder.service("/template/service.java");
                            builder.serviceImpl("/template/serviceImpl.java");
                        }
                )
                // 使用Freemarker引擎模板,默认的是Velocity引擎模板
                .templateEngine(new FreemarkerTemplateEngine())
                .execute();
    }

3.4.自定义生成的代码模板

在上面2.3示例中通过使用templateConfig指定了自定义模板路径,
从依赖的代码生成器中的templates目录下找到了.ftl后缀需要自定义修改的模板文件然后复制项目resources目录的template目录下
在这里插入图片描述
在这里插入图片描述

默认的controller.java.ftl模板中没有定义接口信息,我们可以自定义一些CRUD接口
在这里插入图片描述
添加后的

package ${package.Controller};

import org.springframework.web.bind.annotation.RequestMapping;
<#if restControllerStyle>
import org.springframework.web.bind.annotation.RestController;
<#else>
import org.springframework.stereotype.Controller;
</#if>
<#if superControllerClassPackage??>
import ${superControllerClassPackage};
</#if>

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;
/**
* @description
* @author ${author}
* @createTime ${date}
**/
<#if restControllerStyle>
@RestController
<#else>
@Controller
</#if>
@RequestMapping("<#if package.ModuleName?? && package.ModuleName != "">/${package.ModuleName}</#if>/<#if controllerMappingHyphenStyle>${controllerMappingHyphen}<#else>${table.entityPath}</#if>")
<#if kotlin>
class ${table.controllerName}<#if superControllerClass??> : ${superControllerClass}()</#if>
<#else>
<#if superControllerClass??>
public class ${table.controllerName} extends ${superControllerClass} {
<#else>
public class ${table.controllerName} {
</#if>
    @Autowired
    private ${table.serviceName} ${(table.serviceName)?uncap_first};

    /**
    * 根据id查询
    */
    @GetMapping("/{id}")
    public ${table.entityName} getById(@PathVariable Integer id) {
        return ${(table.serviceName)?uncap_first}.getById(id);
    }

    /**
    * 查询所有
    */
    @GetMapping
    public List<${table.entityName}> list() {
        return ${(table.serviceName)?uncap_first}.list();
    }

    /**
    * 添加
    */
    @PostMapping
    public boolean add(${table.entityName} ${(table.entityName)?uncap_first}) {
        return ${(table.serviceName)?uncap_first}.save(${(table.entityName)?uncap_first});
    }

    /**
    * 修改
    */
    @PutMapping
    public boolean modify(${table.entityName} ${(table.entityName)?uncap_first}) {
        return ${(table.serviceName)?uncap_first}.updateById(${(table.entityName)?uncap_first});
    }

    /**
    * 根据Id删除
    */
    @DeleteMapping("/{id}")
    public boolean deleteId(@PathVariable Integer id) {
       return  ${(table.serviceName)?uncap_first}.removeById(id);
    }

}
</#if>

3.5.运行main函数生成代码

看到下面的日志就代表模板文件生成成功了.
在这里插入图片描述

每个表对应会生成一个controller->service->mapper>xml>entity
生成的结构如下
在这里插入图片描述

可以查看到控制器层已根据自定义模板配置生成了一些CRUD接口
在这里插入图片描述

4.2.mybatis-plus配置类

4.2.1.设置分页插件

在xml中使用分页查询的时候需要用到

@Configuration
public class MybatisPlusConfig {

    /**
     * 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }

}

4.2.2.元数据填充

在上文3.2.创建模板实体类继承通用父类中在父类里使用到的 @TableField(fill = FieldFill.INSERT)和 @TableField(fill = FieldFill.INSERT_UPDATE),这个需要通过配置需要生成默认值

  • FieldFill.INSERT和FieldFill.INSERT_UPDATE属性会在插入数据的时候会调用insertFill函数
  • FieldFill.UPDATE和FieldFill.INSERT_UPDATE属性会在修改的时候调用updateFill函数
@Component
public class MybatisMetaObjectHandlerConfig implements MetaObjectHandler {

    /**
     * 插入数据添加默认值
     */
    @Override
    public void insertFill(MetaObject metaObject) {
        // 该属性为空,可以进行填充
        this.setFieldValByName("createTime", new Date(), metaObject);
        this.setFieldValByName("updateTime", new Date(), metaObject);
    }

    /**
     * 修改数据添加默认值
     */
    @Override
    public void updateFill(MetaObject metaObject) {
        this.setFieldValByName("updateTime", new Date(), metaObject);
    }

}

5.使用mybatis-plus实现基本的CRUD功能

5.1.通用代码层详解

下面的代码会在模板生成器生成模板的时候会自动添加上
service代码详解
此处继承了ServiceImpl通用代码实现类

@Service
public class SysUsersServiceImpl extends ServiceImpl<SysUsersMapper, SysUsers> implements SysUsersService {

}

查看ServiceImpl 源码能看到已经定义了一些通用的代码

//新增  这个有点迷,我以为会和Jpa一样自动判断添加和修改,这个save只能添加
boolean save(T entity);

//根据id获取
T getById(Serializable id)

//根据条件查询
List<T> list(Wrapper<T> queryWrapper)

//分页查询
<E extends IPage<T>> E page(E page, Wrapper<T> queryWrapper);

//批量新增 (默认是1000条,可以调用saveBatch(Collection<T> entityList, int batchSize)指定批量操作数量)
boolean saveBatch(Collection<T> entityList)

//添加或者修改
boolean saveOrUpdate(T entity);

//批量添加或者修改
boolean saveOrUpdateBatch(Collection<T> entityList)

//根据ID删除
removeById(Serializable id)

//根据Id批量删除
boolean removeByIds(Collection<? extends Serializable> idList)


mapper接口
此处继承框架内的BaseMappe 接口,里面也定义了一些通用的接口

public interface SysUserMapper extends BaseMapper<SysUser> {

}

在这里插入图片描述

5.2.自定义xml分页查询

  • 在查询的时候通过指定Page参数自动分页
  • 在查询的时候通过@Param注解指定查询条件别名为condition
@Mapper
public interface SysUsersMapper extends BaseMapper<SysUsers> {
    IPage<SysUsers> page(Page page,@Param("condition") SysUsers condition);
}

在SysUsersMapper.xml中添加下面的代码

    <select id="page" resultType="com.ljm.boot.mybatisplus.entity.SysUsers">
        SELECT *
        FROM sys_users
        where
        1=1
        <if test="condition.username!=null and condition.username!=''">
            AND username LIKE CONCAT('%',#{condition.username},'%')
        </if>
        <if test="condition.password!=null and condition.password!=''">
            AND password=#{condition.password}
        </if>
    </select>

5.3.配置application.yml

server:
  port: 8014
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/boot_master?createDatabaseIfNotExist=true&useSSL=false&serverTimezone=GMT%2b8&characterEncoding=utf8&connectTimeout=10000&socketTimeout=3000&autoReconnect=true
    username: root
    password: 123456
    #项目启动执行sql脚本
    schema: classpath:sql/init.sql
    sql-script-encoding: utf-8
    platform: mysql
    initialization-mode: always
  freemarker:
    #关闭模板缓存,方便测试
    cache: false

# mybatis-plus相关配置
mybatis-plus:
  # xml扫描,多个目录用逗号或者分号分隔(告诉 Mapper 所对应的 XML 文件位置)
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.ljm.boot.mybatisplus.model
  # 以下配置均有默认值,可以不设置
  global-config:
    db-config:
      #主键类型  auto:"数据库ID自增" 1:"用户输入ID",2:"全局唯一ID (数字类型唯一ID)", 3:"全局唯一ID UUID";
      id-type: auto
      #字段策略 IGNORED:"忽略判断"  NOT_NULL:"非 NULL 判断")  NOT_EMPTY:"非空判断"
      field-strategy: NOT_EMPTY
      #数据库类型
      db-type: MYSQL
  configuration:
    # 是否开启自动驼峰命名规则映射:从数据库列名到Java属性驼峰命名的类似映射
    map-underscore-to-camel-case: true
    # 如果查询结果中包含空值的列,则 MyBatis 在映射的时候,不会映射这个字段
    call-setters-on-nulls: true
    # 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl


5.4.在SpringBoot测试中使用CRUD方法

@SpringBootTest
class MybatisPlusApplicationTests {

    @Autowired
    private SysUsersService sysUsersService;

    @Resource
    private SysUsersMapper sysUsersMapper;

    @Test
    void contextLoads() {
        //查询
        {
            //指定查询列和查询条件
            LambdaQueryWrapper<SysUsers> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            lambdaQueryWrapper
                    //只查询id,姓名,年纪三个字段的数据
                    .select(SysUsers::getId, SysUsers::getUsername, SysUsers::getGrade)
                    //根据年级等值查询
                    .eq(SysUsers::getGrade, 7)
                    //模糊查询
                    .like(SysUsers::getUsername, "%test%")
                    //根据Id范围查询
                    .in(SysUsers::getId, Arrays.asList(1));

            //根据Id查询
            SysUsers sysUsers = sysUsersService.getById(1);
            //根据查询条件获取一条数据
            sysUsers = sysUsersService.getOne(lambdaQueryWrapper);
            System.out.println(sysUsers == null);

            //根据条件查询数据列表数据
            List<SysUsers> userList = sysUsersService.list(lambdaQueryWrapper);
            System.out.println(userList.size());

            //页码,每页显示的记录数
            Integer currentPage = 1, pageSize = 10;
            Page page = new Page<>(currentPage, pageSize);
            //分页查询
            IPage<SysUsers> iPage = sysUsersService.page(page);
            System.out.printf("总页数:{%d},总记录数:{%d}", iPage.getPages(), iPage.getTotal());
            System.out.println();
            //分页指定查询条件
            //IPage<SysUser> iPage = sysUserService.page(page, lambdaQueryWrapper);

            //封装查询条件用于xml的自定义查询
            SysUsers condition = new SysUsers();
            condition.setUsername("test");
            condition.setPassword("123456");

            //自定义xml分页查询 会写sql就行
            IPage<SysUsers> iPage2 = sysUsersMapper.page(page, condition);
            System.out.printf("自定义xml分页查询 总页数:{%d},总记录数:{%d}", iPage2.getPages(), iPage2.getTotal());
        }
        //增删改
        {
            SysUsers user = new SysUsers();
            user.setUsername("test99");

            //增加
            boolean flag = sysUsersService.save(user);
            System.out.println("save flag=" + flag);
            //批量增加
            //flag = sysUsersService.saveBatch(Arrays.asList(user));
            System.out.println("save flag=" + flag);

            //修改
            SysUsers updateUser = new SysUsers();
            updateUser.setPassword("1234567");
            updateUser.setId(1);

            //根据id修改
            flag = sysUsersService.updateById(updateUser);
            System.out.println("updateById flag=" + flag);

            UpdateWrapper<SysUsers> sysUsersUpdateWrapper = Wrappers.update();
            sysUsersUpdateWrapper.lambda()
                    .set(SysUsers::getUsername, "admin")
                    .eq(SysUsers::getId, 1);
            flag = sysUsersService.update(sysUsersUpdateWrapper);
            System.out.println("update flag=" + flag);
            //删除
            //根据Id删除
            flag = sysUsersService.removeById(2);
            System.out.println("removeById flag=" + flag);
            //根据Id批量删除
            flag = sysUsersService.removeBatchByIds(Arrays.asList(3, 4, 5));
            System.out.println("removeBatchByIds flag=" + flag);
            //根据自定义条件删除
            LambdaQueryWrapper<SysUsers> condition = new LambdaQueryWrapper<>();
            condition.eq(SysUsers::getGrade, 9);
            flag = sysUsersService.remove(condition);
            System.out.println("removeBatchByIds flag=" + flag);

        }
    }

}

6.项目配套代码

gitee代码地址

创作不易,要是觉得我写的对你有点帮助的话,麻烦在gitee上帮我点下 Star

【SpringBoot框架篇】其它文章如下,后续会继续更新。

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

皓亮君

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值