MyBatis-Plus

97 阅读8分钟

简介

直接看官网

快速入门

前期准备工作

  1. 创建一个数据库mybatis_plus
  2. 创建User表,在表中插入数据
-- 创建数据库
CREATE DATABASE IF NOT EXISTS mybatis_plus
-- 创建user表
CREATE TABLE USER
(
	id BIGINT(20) NOT NULL COMMENT '主键ID',
	NAME VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
	age INT(11) NULL DEFAULT NULL COMMENT '年龄',
	email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
	PRIMARY KEY (id)
);
-- 插入数据
INSERT INTO USER (id, NAME, age, email) VALUES
(1, 'Jone', 18, 'test1@baomidou.com'),
(2, 'Jack', 20, 'test2@baomidou.com'),
(3, 'Tom', 28, 'test3@baomidou.com'),
(4, 'Sandy', 21, 'test4@baomidou.com'),
(5, 'Billie', 24, 'test5@baomidou.com');

mybatis-plus快速入门

  1. 创建一个SpringBoot项目mybatis_plus
  2. 导入依赖
    <dependencies>
        <!--数据库-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!--mybatis-plus-->
        <!--mybatis-plus和mybatis最好不要一起导入-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.2</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
  1. IDEA连接数据库
  2. application.properties配置数据库
# 数据库配置
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.data-username=root
spring.datasource.data-password=123456
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
  1. 创建pojo包,在包下创建实体类user,实现OR映射
package com.example.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

  1. 创建mapper包,在包下创建UserMapper接口
package com.example.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.pojo.User;
import org.springframework.stereotype.Repository;

//BaseMapper完成了所有基本的CRUD
@Repository // 持久层 注入到bean
public interface UserMapper extends BaseMapper<User> {

}

  1. 在主启动类加上@MapperScan("com.example.mapper"),用来扫描 mapper 文件夹
  2. 编写测试类测试
package com.example;

import com.example.mapper.UserMapper;
import com.example.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

@SpringBootTest
class MybatisPlusApplicationTests {

    @Autowired
    private UserMapper userMapper;

    @Test
    void contextLoads() {
        // 查询全部用户
        // 参数是一个Wrapper,条件构造器,我们先填null,不用它
        List<User> users = userMapper.selectList(null);
        for (User user : users) {
            System.out.println(user);
        }
    }

}

  1. 测试结果:

image.png

这里直接就查询出来,但是我们不知道它是怎么执行查询的,这时候我们就需要用到日志

怎么配置日志呢?

很简单,直接在application.properties文件配置一下日志就可以了。

mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

我们这里选择最简单的控制台输出日志 image.png

注意:

如果我们使用log4J啊什么的,要记得导入他们的依赖!!!

这时候,我们在启动测试,查看一下控制台的输出

image.png

MyBatis-Plus---CRUD扩展

插入测试

  1. 直接编写测试类
    @Test
    void insertTest(){
        User user = new User();
        user.setName("小米");
        user.setAge(23);
        user.setEmail("test@qq.com");

        int rs = userMapper.insert(user);
        System.out.println(rs);//插入结果
        System.out.println(user);//查看我们插入的用户
    }
  1. 结果

image.png

--> 发现,我们没有给主键id传值,它自动帮我们传了一个值!这个值就是分布式数据库的全局唯一id

这就涉及到了主键生成策略

不同主键策略的测试

在实体类上通过@TableId()注解来实现不同的主键策略的测试

image.png

三种废弃的

image.png

  1. 手动输入:@TableId(type = IdType.INPUT) 没有插入id,报错

image.png

手动插入id,成功

image.png

  1. 自增: @TableId(type = IdType.AUTO)

启动测试,你会发现,会出现报错

image.png image.png

注意点:当你使用自增策略时,数据库的主键也要勾选自增,一定要勾选

image.png

再启动测试

  1. 雪花算法:@TableId(type = IdType.ASSIGN_ID)

image.png

  1. 更多策略:@TableId()注解详解

查询操作

查询单个用户

    @Test
    void selectTest1(){
        User user = userMapper.selectById(1L);
        System.out.println(user);
    }

image.png

查询多个用户

    @Test
    void selectTest2(){
        List<User> users = userMapper.selectBatchIds(Arrays.asList(1,2,3));
        users.forEach(System.out::println);
    }

image.png

条件查询--map 单条件:

    @Test
    void selectTest3(){
        HashMap<String, Object> map = new HashMap<>();
        // 条件查询
        map.put("name","xiaohong");

        List<User> users = userMapper.selectByMap(map);
        users.forEach(System.out::println);
    }

image.png

多条件:

    @Test
    void selectTest3(){
        HashMap<String, Object> map = new HashMap<>();
        // 条件查询
        map.put("name","xiaohong");
        map.put("age",18);

        List<User> users = userMapper.selectByMap(map);
        users.forEach(System.out::println);
    }

image.png

分页查询

  1. 配置注册插件
    //注册分页插件
    @Bean
    public MybatisPlusInterceptor PaginationInnerInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2));
        return interceptor;
    }
  1. 测试
 // 分页查询
    @Test
    void LimitTest(){
        /** 参数解析:
         *  参数一:当前页
         *  参数二:当前页大小
         */
        Page<User> page = new Page<>(1,5);
        userMapper.selectPage(page,null);

        //当前页信息
        page.getRecords().forEach(System.out::println);
        //查询到的总信息数量
        System.out.println(page.getTotal());
    }

image.png

更新测试

  1. 更新一个属性:name

image.png

  1. 更新两个属性:age+email

image.png

--> 观察两个sql,我们会惊喜的发现,mybatis-plus帮我们实现了动态SQL的拼接

自动填充

作用位置:数据库的表中数据的创建与更新的时间,手动帮我们完成

实现步骤

  1. 在数据库表中填加创建时间和更新时间两个属性

image.png

  1. 在实体类更新属性createTimeupdateTime
private LocalDateTime createTime;
private LocalDateTime updateTime;
  1. 在实体类的createTimeupdateTime两个属性上添加注解
    //插入时更新创建时间
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    //更新时更新,一开始的插入也算更新,所以用INSERT_UPDATE
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;

其他属性: image.png

  1. 编写处理器来处理这两个注解
package com.example.Handler;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;

import java.util.Date;

@Component
public class MyMetaobjectHandler 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);
    }
}

  1. 进行插入测试和更新测试,注意时间的变化。 插入:

image.png

更新:

image.png

  1. 注意点:

乐观锁 OptimisticLockerInnerInterceptor

简介

  • 乐观锁:故名思意十分乐观,它总是认为不会出现问题,无**论干什么不去上锁!**如果出现了问题,再次更新值测试
  • 悲观锁:故名思意十分悲观,它总是认为总是出现问题,**无论干什么都会上锁!**再去操作! 悲观锁 VS 乐观锁

乐观锁实现方式:版本控制

  1. 取出记录时,获取当前version
  2. 更新时,带上这个version
  3. 执行更新时, set version = newVersion where version = oldVersion
  4. 如果version不对,就更新失败

举例说明

比如说,现在有一个线程A要更新id=1的用户

A:

update user set name="haha",version = version + 1 where id = 1 and version = 1;

可以正常更新。但是,如果这时候突然再来一个B线程,插队抢先完成

B:

update user set name="xixi",version = version + 1 where id = 1 and version = 1;

这时候,就会先完成B线程的更新操作,完成之后,version变为2A线程就无法进行更新操作

操作测试

  1. 在数据库添加version字段

image.png

  1. 在对应数据库加上version属性,实现映射
@Version // 代表这是一个乐观锁
private Integer version;
  1. 注册配置乐观锁
    //注册乐观锁
    @Bean
    public MybatisPlusInterceptor OptimisticLockerInnerInterceptor() {
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return mybatisPlusInterceptor;
    }
  1. 编写代码测试
  • 只有A线程的情况下
@Test
void OptimisticLockerTest(){
    //1. 查询用户信息
    User user = userMapper.selectById(1L);
    //2. 修改用户信息
    user.setName("xiaolong");
    //3. 执行更新操作
    userMapper.updateById(user);
}

image.png

  • A、B两个线程都在的情况
@Test
void OptimisticLockerTest2(){
    //A线程
    User user = userMapper.selectById(1L);
    user.setName("xiaohu");

    //B线程  插队,率先执行
    User user2 = userMapper.selectById(1L);
    user2.setName("xiaohong");
    userMapper.updateById(user2);

    userMapper.updateById(user);//如果没有乐观锁,就会覆盖掉B线程的值
}

image.png

删除操作

物理删除

物理删除:直接从数据库删除

原先数据库数据 image.png

删除单用户

@Test
    void deleteTest1(){
        userMapper.deleteById(1385936574033739778L);
    }

image.png

删除多用户

@Test
    void deleteTest2(){
        userMapper.deleteBatchIds(Arrays.asList(1385936784428417025L,1385936784428417026L));
    }

image.png

条件删除--map

@Test
    void deleteTest3(){
        HashMap<String, Object> map = new HashMap<>();
        map.put("name","小米");
        map.put("age",25);
        userMapper.deleteByMap(map);
    }

image.png

逻辑删除

逻辑删除:在数据库中没有被删除,但是通过一个变量让本该被删除的数据失效

实现步骤

  1. 在数据库中加deleted字段,默认值为0

image.png

  1. 在实体类编写对应的属性
@TableLogic // 逻辑删除
private Integer deleted;
  1. 配置逻辑删除
# 配置逻辑删除
mybatis-plus.global-config.db-config.logic-not-delete-value=0
mybatis-plus.global-config.db-config.logic-delete-value=1
  1. 测试
  • 我们先删除id=1的用户

image.png

  • 我们再查询id=1的用户

image.png

逻辑删除成功~~~~

条件构造器Wrapper

相信在之前的代码编写中,大家对于Wrapper这个参数有很多疑问,Wrapper到底是干嘛的呢?

很简单,一句话,Wrapper就是用来编写复杂的SQL语句的。

直接上代码体验一下这几种常见的SQL复杂语句。

    @Test
    void contextLoads() {
        // 查询name不为空的用户,并且邮箱不为空的用户,年龄大于等于20
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.isNotNull("name").isNotNull("email")
                .ge("age", 20);
        userMapper.selectList(wrapper).forEach(System.out::println); // 和我们刚才学习 的map对比一下 }
    }

    @Test
    void test2() {
        // 查询名字是小米的用户
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("name", "小米");
        User user = userMapper.selectOne(wrapper); // 查询一个数据,出现多个结果使用List 或者 Map
        System.out.println(user);
    }

    @Test
    void test3() {
        // 查询年龄在 18 ~ 23 岁之间的用户
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.between("age", 18, 23); // 区间
        Integer count = userMapper.selectCount(wrapper);// 查询结果数
        System.out.println(count);
    }

    @Test
    void test4() {
        // 查询名字没有a,且名字中以T开头的用户
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        // notLike 没有
        // likeRight   T%
        //likeLeft     %T
        wrapper.notLike("name", "a")
                .likeRight("name", "T");

        List<Object> objects = userMapper.selectObjs(wrapper);
        objects.forEach(System.out::println);
    }

    //测试六
    @Test
    void test6() {
        // 通过id进行排序 wrapper.orderByAsc("id");
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        List<User> users = userMapper.selectList(wrapper);
        users.forEach(System.out::println);
    }

关于wrapper的使用,还有很多,远远不止上面这几种,还有很多,详情可以看官网

代码自动生成器

简介

AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码,极大的提升了开发效率。

一句话说明,帮我们写代码的。

实现步骤

导入依赖

  1. 添加 代码生成器 依赖
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.4.1</version>
</dependency>
  1. 添加 模板引擎 依赖,MyBatis-Plus 支持 Velocity(默认)、Freemarker、Beetl,用户可以选择自己熟悉的模板引擎,如果都不满足您的要求,可以采用自定义模板引擎。这里选择它默认的就行
<dependency>
    <groupId>org.apache.velocity</groupId>
    <artifactId>velocity-engine-core</artifactId>
    <version>2.3</version>
</dependency>

编写配置 自己在测试类中创建一个自动编写的类

package com.example;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.po.TableFill;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;

import java.util.ArrayList;


import java.util.ArrayList;

public class AutoToEncoding {

    public static void main(String[] args) {
        // 需要构建一个 代码自动生成器 对象
        AutoGenerator mpg = new AutoGenerator();
        // 配置策略
        // 1、全局配置
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir");

        gc.setOutputDir(projectPath + "/src/main/java");
        gc.setAuthor("哈哈");
        gc.setOpen(false);
        gc.setFileOverride(false); // 是否覆盖
        gc.setServiceName("%sService"); // 去Service的I前缀
        gc.setIdType(IdType.ID_WORKER);
        gc.setDateType(DateType.ONLY_DATE);
        gc.setSwagger2(true);
        mpg.setGlobalConfig(gc);
        //2、设置数据源
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/autoEncoding?useSSL = false & useUnicode = true & characterEncoding = utf-8 & serverTimezone = UTC");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("123456");
        dsc.setDbType(DbType.MYSQL);
        mpg.setDataSource(dsc);
        //3、包的配置
        PackageConfig pc = new PackageConfig();
        pc.setModuleName("blog");
        pc.setParent("com.kuang");
        pc.setEntity("entity");
        pc.setMapper("mapper");
        pc.setService("service");
        pc.setController("controller");
        mpg.setPackageInfo(pc);
        //4、策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setInclude("表名"); // 设置要映射的表名,这里可以填多个
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        strategy.setEntityLombokModel(true); // 自动lombok;
        strategy.setLogicDeleteFieldName("deleted");
        // 自动填充配置
        TableFill gmtCreate = new TableFill("gmt_create", FieldFill.INSERT);
        TableFill gmtModified = new TableFill("gmt_modified",
                FieldFill.INSERT_UPDATE);
        ArrayList<TableFill> tableFills = new ArrayList<>();
        tableFills.add(gmtCreate);
        tableFills.add(gmtModified);
        strategy.setTableFillList(tableFills);
        // 乐观锁
        strategy.setVersionFieldName("version");
        strategy.setRestControllerStyle(true);
        strategy.setControllerMappingHyphenStyle(true); //localhost:8080/hello_id_2
        mpg.setStrategy(strategy);
        mpg.execute(); //执行
    }

}

主要修改设置strategy.setInclude("表名"); 其他的都可以照抄