MyBatis-Plus 学习笔记 -- 从入门到实战,告别 SQL 语句

碎碎几句: MyBatis-Plus 是什么,如果不知道这个,那么你一定听到过 Iphone-plus,顾名思义,MyBatis-plus 是 Mybatis 的升级版,在使用Mybatis 的时候,对于建的 CRUD,我们还需要进行手动编写,为简化开发而生的 Mybatis-plus 将这些不费脑子的步骤进行融合,使得我们的开发更加关注于事务的逻辑。

MyBatis-Plus 学习笔记 -- 从入门到实战,告别 SQL 语句_第1张图片

学习过程中看的视频 狂神说MyBatisPlus,参考的文档Mybatis-Plus2.xxx,如果你使用的是 3.x 可能有些注解会显示过时,所以如果你跟我一样看的狂神的视频,可以将我的 pom 中的依赖 CV 到你的项目中。

文章目录

    • 1、快速入门:
    • 2、配置日志输出:
    • 3、CRUD 扩展:
      • 3.1 插入操作:
      • 3.2 主键生成策略:
      • 3.3 更新操作:
      • 3.4 自动填充:
      • 3.5 乐观锁:
      • 3.6 查询操作:
      • 3.7 分页查询:
      • 3.8 删除操作:
      • 3.9 逻辑删除:
    • 4、性能分析插件:
    • 5、条件构造器:
    • 6、代码自动生成器:

1、快速入门:

创建数据库 mybatisplus。

创建表 user:

    DROP TABLE IF EXISTS 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)
    );

添加数据:

    DELETE FROM user;

    INSERT INTO user (id, name, age, email) VALUES
    (1, 'Jone', 18, '[email protected]'),
    (2, 'Jack', 20, '[email protected]'),
    (3, 'Tom', 28, '[email protected]'),
    (4, 'Sandy', 21, '[email protected]'),
    (5, 'Billie', 24, '[email protected]');

添加相关依赖:

    <dependencies>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <optional>trueoptional>
        dependency>
        <dependency>
            <groupId>com.baomidougroupId>
            <artifactId>mybatis-plus-boot-starterartifactId>
            <version>3.0.5version>
        dependency>
        <dependency>
            <groupId>com.h2databasegroupId>
            <artifactId>h2artifactId>
            <scope>runtimescope>
        dependency>

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintagegroupId>
                    <artifactId>junit-vintage-engineartifactId>
                exclusion>
            exclusions>
        dependency>
    dependencies>

配置数据库相关信息:

spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mybatisplus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT

创建实体类: User:>

package com.pxl.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.springframework.stereotype.Component;

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

创建 UserDao 接口 实现 BaseMapper< User> 接口:

package com.pxl.dao;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.pxl.entity.User;
import org.springframework.stereotype.Repository;

@Repository
public interface UserDao extends BaseMapper<User> {
     
}

添加 MapperScan 注解,扫描 UserDao:

package com.pxl;

import org.mybatis.spring.annotation.MapperScan;
import org.mybatis.spring.annotation.MapperScans;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("com.pxl.dao")
public class MybatisPlusApplication {
     

    public static void main(String[] args) {
     
        SpringApplication.run(MybatisPlusApplication.class, args);
    }

}

测试类:

package com.pxl;

import com.pxl.dao.UserDao;
import com.pxl.entity.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 {
     

    // 创建一个 userDao 的对象
    @Autowired
    private UserDao userDao;
    @Test
    void contextLoads() {
     
        List<User> users = userDao.selectList(null);
        for (User user : users) {
     
            System.out.println(user);
        }
    }

}

MyBatis-Plus 学习笔记 -- 从入门到实战,告别 SQL 语句_第2张图片

2、配置日志输出:

在日常的 debug 和开发中, 学会看日志,分析日志内容弄是一个合格的程序员所必备的知识。

在 application.properties 文件中添加如下内容:

# 配置日志输出
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

MyBatis-Plus 学习笔记 -- 从入门到实战,告别 SQL 语句_第3张图片

3、CRUD 扩展:

3.1 插入操作:

insert:

    @Test
    public void testInsert() {
     
        User user = new User();
        user.setName("潘小蓝");
        user.setAge(18);
        user.setEmail("[email protected]");
        int insert = userDao.insert(user);

        System.out.println(insert);
    }

分析一下日志:

MyBatis-Plus 学习笔记 -- 从入门到实战,告别 SQL 语句_第4张图片

Mybatis-Plus 自动实现了回填的功能,而且 id 在大多数情况下都是 唯一的。

3.2 主键生成策略:

默认 ID_WORKER 是全局唯一 id。

雪花算法: 生成全局唯一 id 的一种算法,有兴趣的小伙伴可以去了解一下。

自增方式生成 id:

    // long 的包装类是 Long
    @TableId(type = IdType.AUTO)
    private Long id;

在这里插入图片描述

其余的源码解释:

public enum IdType {
     
    AUTO(0),                   // 数据库 id 自增
    NONE(1),				   // 未设置主键
    INPUT(2),				   // 手动输入
    ASSIGN_ID(3),			   // 生成全局唯一 id (雪花算法)
    ASSIGN_UUID(4),			   // 生成全局唯一 id (uuid)
    /** @deprecated */
    @Deprecated
    ID_WORKER(3),
    /** @deprecated */
    @Deprecated
    ID_WORKER_STR(3),
    /** @deprecated */
    @Deprecated
    UUID(4);

    private final int key;

    private IdType(int key) {
     
        this.key = key;
    }

    public int getKey() {
     
        return this.key;
    }
}

3.3 更新操作:

    @Test
    public void testUpdate() {
     
        User user = new User();
        user.setId(1L);
        user.setName("李小桂");
        user.setAge(20);

        // 通过 id 更新信息,参数是一个对象
        userDao.updateById(user);
    }

在这里插入图片描述

3.4 自动填充:

在实际工作中,有些属性是自动化完成的,我们不希望手动更新。在 阿里巴巴的手册上,所有的数据表:gmt_create,gmt_modified 几乎所有的表都要配置上,而且需要自动化。

方式一:数据库级别:

  • 在表中新增字段 create_time,update_time。

MyBatis-Plus 学习笔记 -- 从入门到实战,告别 SQL 语句_第5张图片

  • 在 User 类中添加属性:

        private Date createTime;
        private Date updateTime;
    

在这里插入图片描述

方式二:代码级别:

  1. 删除 数据库中的默认值。

  2. 在 User 类中的属性中加入注解:

        @TableField(fill = FieldFill.INSERT)
        private Date createTime;
        @TableField(fill = FieldFill.INSERT_UPDATE)
        private Date updateTime;
    
  3. 编写处理器对注解进行处理:

    package com.pxl.handler;
    
    import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.ibatis.reflection.MetaObject;
    import org.springframework.stereotype.Component;
    
    import java.util.Date;
    
    // 日志操作
    @Slf4j
    // 将这个类注入到 IOC 容器中
    @Component
    public class MyMetaObjectHandler implements MetaObjectHandler {
           
        @Override
        public void insertFill(MetaObject metaObject) {
           
            log.info("start insert .....");
            this.setFieldValByName("createTime",new Date(),metaObject);
            this.setFieldValByName("updateTime",new Date(),metaObject);
        }
    
        @Override
        public void updateFill(MetaObject metaObject) {
           
            log.info("start update ...........");
            this.setFieldValByName("updateTime",new Date(),metaObject);
        }
    }
    
    

在这里插入图片描述

3.5 乐观锁:

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

关于乐观锁与悲观锁的区别及使用在这篇博客中有详尽的解释:面试必备之乐观锁与悲观锁。

简单理解一下就是:

乐观锁: 这个锁是比较乐观的,它认为每个任务都是安全的,都不用上锁,如果出现了问题,就驳回再次进行更新值进行测试。

悲观锁: 从字面意思可以看出,这个锁是悲观的,看待每件事情都是先往坏的方面考虑,认为每个事务都是有风险的,都会将其上锁,只有当当前任务完成后,才会开锁。

乐观锁实现方式:

  • 取出记录时,获取当前的 version。
  • 更新时,带上这个 version。
  • 执行更新时, set version = newVersion where version = oldVersion。
  • 如果条件不对,就更新失败,重新赋值进行更新。

测试乐观锁的插件:

  • 给数据库中增加 version 字段,并且设置初始值为 1

MyBatis-Plus 学习笔记 -- 从入门到实战,告别 SQL 语句_第6张图片

  • 在 User 类中添加对应的属性,并且添加 乐观锁的注解:

        // 乐观锁添加注解
        @Version
        private int version;
    
  • 在配置类中注册 乐观锁的 插件:

    package com.pxl.config;
    
    import com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor;
    import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.transaction.annotation.EnableTransactionManagement;
    
    @EnableTransactionManagement
    @MapperScan("com.pxl.dao")
    @Configuration
    public class MyConfig {
           
        @Bean
        public OptimisticLockerInterceptor OptimisticLockerInnerInterceptor() {
           
            return new OptimisticLockerInterceptor();
        }
    }
    
    
  • 测试:

        @Test
    	// 测试乐观锁成功
        public void testOptimistic() {
           
            User user = userDao.selectById(2L);
            user.setName("潘小玮");
            user.setAge(18);
    
            userDao.updateById(user);
    
        }
    

MyBatis-Plus 学习笔记 -- 从入门到实战,告别 SQL 语句_第7张图片

```java
    @Test
    // 测试乐观锁失败
    public void testOptimistic2() {
        // 线程 1
        User user = userDao.selectById(1L);
        user.setName("xiaolan111");
        // 线程插队
        User user1 = userDao.selectById(1L);
        user1.setName("xiaolan222");

        userDao.updateById(user1);

        userDao.updateById(user);


    }
```

- 有乐观锁:

在这里插入图片描述

- 无乐观锁:

在这里插入图片描述

我们发现,没有乐观锁时,后面的线程结果会覆盖前面线程的结果,当我们更新某条记录的时候,发现这条记录已经被更新了,这就是 乐观锁的作用,用于判别。

3.6 查询操作:

    // 简单查询
    @Test
    public void testSelect() {
     
        // 根据 id 进行查询
        User user = userDao.selectById(1l);
        System.out.println(user);

        List<User> users = userDao.selectList(null);
        for (User user1 : users) {
     
            System.out.println(user1);
        }
    }

    // 批量查询
    @Test
    public void testSelect2() {
     
        List<User> users = userDao.selectBatchIds(Arrays.asList(1, 2, 3));
        for (User user : users) {
     
            System.out.println(user);
        }
    }

    // 条件查询
    @Test
    public void testSelect3() {
     
        Map<String,Object> map = new HashMap<String,Object>();

        // map 用来保存各个条件, MP 会进行自动拼接
        map.put("age",18);

        List<User> users = userDao.selectByMap(map);

        for (User user : users) {
     
            System.out.println(user);
        }


    }

3.7 分页查询:

分页在网页的各个地方随处可见,随之而来的插件也是应有尽有。

MyBatis-Plus 也有自身内置的分页插件,我们在此使用其内置的分页插件。

  • 注册插件到 Spring 的 IOC 容器中。

        // 注册分页插件到 IOC 容器中
        @Bean
        public PaginationInterceptor PaginationInterceptor() {
           
            return new PaginationInterceptor();
        }
    
  • 利用 Page 对象进行分页查询。

        // 分页查询
        @Test
        public void testPageHelper() {
           
            /**
             *  参数1 : 当前页
             *  参数2 : 页面大小
             */
            Page<User> page = new Page<>(1, 3);
            // 查询数据库
            Page<User> userPage = userDao.selectPage(page, null);
            // 获取查询到的结果
            List<User> records = userPage.getRecords();
            // 输出结果
            for (User user : records) {
           
                System.out.println(user);
            }
        }
    
    

3.8 删除操作:

    // 简单删除
    @Test
    public void testDelete(){
     
        int result = userDao.deleteById(3L);
        System.out.println(result);
    }

    // 批量删除
    @Test
    public void testDelete2() {
     
        int result = userDao.deleteBatchIds(Arrays.asList(1323889317277933569L, 							1323889317277933571L));
        System.out.println(result);
    }

    // 条件删除
    @Test
    public void testDelete3() {
     
        Map<String,Object> maps = new HashMap<>();
        maps.put("age",18);
        int i = userDao.deleteByMap(maps);
        System.out.println(i);
    }

3.9 逻辑删除:

在数据库中数据信息没有被移除,而是通过一个变量让这条数据失效。

  • 在数据库中添加一个字段 deleted。

MyBatis-Plus 学习笔记 -- 从入门到实战,告别 SQL 语句_第8张图片

  • 在 User 类中添加 deleted 属性。

    // 逻辑删除
    @TableLogic
    private Integer deleted;
    
  • 在全局配置文件中添加如下属性。

    # 逻辑删除
    mybatis-plus.global-config.db-config.logic-delete-value=0
    mybatis-plus.global-config.db-config.logic-not-delete-value=1
    
  • 测试:

        @Test
        public void testLogicDelete() {
           
            // 删除操作
            int i = userDao.deleteById(1L);
            System.out.println(i);
            // 查询是否删除完成
            List<User> users = userDao.selectList(null);
            for (User user : users) {
           
                System.out.println(user);
            }
        }
    

MyBatis-Plus 学习笔记 -- 从入门到实战,告别 SQL 语句_第9张图片

MyBatis-Plus 学习笔记 -- 从入门到实战,告别 SQL 语句_第10张图片

4、性能分析插件:

在项目的上线过程中,项目的运行效率是我们经常要考虑的一个因素,项目一旦上线,修改起来相对麻烦,可以选择在开发过程或者测试过程中对性能进行分析,从而优化其代码。

在这里主要使用性能分析拦截器:用于输出每条SQL 语句及其执行时间。

  • 导入插件:

    	 // 性能分析插件
        @Bean
        @Profile({
           "dev", "test"})// 设置 dev test 环境开启
        public PerformanceInterceptor performanceInterceptor() {
           
            performanceInterceptor().setMaxTime(1);
            performanceInterceptor().setFormat(true);
            return new PerformanceInterceptor();
        }
    
  • 测试:

        @Test
        public void testPerformance() {
           
            List<User> users = userDao.selectList(null);
    //        System.out.println(user);
            for (User user : users) {
           
                System.out.println(user);
            }
        }
    

5、条件构造器:

实体包装器,用于处理 sql 拼接,排序,实体参数查询等!

使用的是数据库字段,不是Java属性!

  • 条件参数说明:
查询方式 说明
setSqlSelect 设置 SELECT 查询字段
where WHERE 语句,拼接 + WHERE 条件
and AND 语句,拼接 + AND 字段=值
andNew AND 语句,拼接 + AND (字段=值)
or OR 语句,拼接 + OR 字段=值
orNew OR 语句,拼接 + OR (字段=值)
eq 等于=
allEq 基于 map 内容等于=
ne 不等于<>
gt 大于>
ge 大于等于>=
lt 小于<
le 小于等于<=
like 模糊查询 LIKE
notLike 模糊查询 NOT LIKE
in IN 查询
notIn NOT IN 查询
isNull NULL 值查询
isNotNull IS NOT NULL
groupBy 分组 GROUP BY
having HAVING 关键词
orderBy 排序 ORDER BY
orderAsc ASC 排序 ORDER BY
orderDesc DESC 排序 ORDER BY
exists EXISTS 条件语句
notExists NOT EXISTS 条件语句
between BETWEEN 条件语句
notBetween NOT BETWEEN 条件语句
addFilter 自由拼接 SQL
last 拼接在最后,例如:last(“LIMIT 1”)

测试一: 查询 name 不为空的用户,并且邮箱不为空的用户,年龄 >= 22.

    @Test
    public void test1() {
     
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper
                .isNotNull("name")
                .isNotNull("email")
                .ge("age",22);
        List<User> users = userDao.selectList(wrapper);
        for (User user : users) {
     
            System.out.println(user);
        }
    }

测试二:查询名字潘小猪:

    @Test
    public void test2() {
     
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("name","潘小猪");
        List<User> users = userDao.selectList(wrapper);
        for (User user : users) {
     
            System.out.println(user);
        }
    }

测试三:查询年龄在 20 ~ 30 岁之间的用户:

    @Test
    public void test3() {
     
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper
                .between("age","20","30");
        List<User> users = userDao.selectList(wrapper);
        for (User user : users) {
     
            System.out.println(user);
        }
    }

测试四:模糊查询:

    @Test
    public void test4() {
     
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper
                // name 中不包含 e 的
                .notLike("name","e")
                .likeRight("email","t");
        List<User> users = userDao.selectList(wrapper);
        for (User user : users) {
     
            System.out.println(user);
        }
    }

测试五:id 在子查询中查询出来:

    // 子查询
    @Test
    public void test5() {
     
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper
                .inSql("id","select id from user where id <= 5");
        List<User> users = userDao.selectList(wrapper);
        for (User user : users) {
     
            System.out.println(user);
        }
    }

测试六:通过 id 进行排序:

    @Test
    public void test6() {
     
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper
                // 升序排列
                .orderByAsc("id");
        List<User> users = userDao.selectList(wrapper);
        for (User user : users) {
     
            System.out.println(user);
        }

        wrapper
                // 降序排列
                .orderByDesc("id");
        List<User> users1 = userDao.selectList(wrapper);
        for (User user : users1) {
     
            System.out.println(user);
        }
    }

6、代码自动生成器:

package com.pxl;

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.Properties;

/**
 *  代码自动生成器
 */
public class MyCode {
     
    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("panxiaolan");

        gc.setOpen(false);
        // 是否覆盖
        gc.setFileOverride(false);
        // 去 Service 的 I 前缀
        gc.setServiceName("%sService");
        // 设置 id 的生成策略
        gc.setIdType(IdType.ID_WORKER);

        gc.setDateType(DateType.ONLY_DATE);
        gc.setSwagger2(true);

        mpg.setGlobalConfig(gc);

        // 设置数据源
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/mybatisplus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("root");
        dsc.setDbType(DbType.MYSQL);

        mpg.setDataSource(dsc);

        // 包的配置
        PackageConfig pc = new PackageConfig();
        pc.setModuleName("user");
        pc.setParent("com.pxl");
        pc.setMapper("mapper");
        pc.setEntity("pojo");
        pc.setController("controller");
        pc.setService("service");
        mpg.setPackageInfo(pc);

        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setInclude("user");
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);

        strategy.setEntityLombokModel(true); // 自动 lombok
        // 逻辑删除
        strategy.setLogicDeleteFieldName("deleted");
        TableFill createTime = new TableFill("create_time", FieldFill.INSERT);
        TableFill updateTime = new TableFill("update_time", FieldFill.INSERT_UPDATE);

        // 自动填充
        ArrayList<TableFill> tableFills = new ArrayList<>();
        tableFills.add(createTime);
        tableFills.add(updateTime);
        strategy.setTableFillList(tableFills);

        strategy.setVersionFieldName("version");

        strategy.setRestControllerStyle(true);
        strategy.setControllerMappingHyphenStyle(true);

        mpg.setStrategy(strategy);

        mpg.execute();
    }
}

MyBatis-Plus 学习笔记 -- 从入门到实战,告别 SQL 语句_第11张图片

MyBatis-Plus 学习笔记 -- 从入门到实战,告别 SQL 语句_第12张图片
MyBatis-Plus 学习笔记 -- 从入门到实战,告别 SQL 语句_第13张图片

你可能感兴趣的:(MyBatis,mybatis,mybatis-plus,狂神说,学习笔记)