title: 快速入门
date: ‘2021-12-27 08:22:39’
tags: [Mybatis]
categories:

  • 编程学习
  • 数据库

简介

MyBatisPlus 可以节省工作时间,将 CRUD 操作自动化完成
官网:https://baomidou.com/
Mybatis快速入门 - 图1

快速开始

第三方组件使用基本流程:

  1. 导入依赖
  2. 依赖配置
  3. 代码编写
  4. 扩展技术

步骤

  1. 创建数据库 mybatis_plus
  2. 创建 user 表
  1. DROP TABLE IF EXISTS user;
  2. CREATE TABLE user
  3. (
  4. id BIGINT(20) NOT NULL COMMENT '主键ID',
  5. name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
  6. age INT(11) NULL DEFAULT NULL COMMENT '年龄',
  7. email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
  8. PRIMARY KEY (id)
  9. );
  10. INSERT INTO user (id, name, age, email) VALUES
  11. (1, 'Jone', 18, 'test1@baomidou.com'),
  12. (2, 'Jack', 20, 'test2@baomidou.com'),
  13. (3, 'Tom', 28, 'test3@baomidou.com'),
  14. (4, 'Sandy', 21, 'test4@baomidou.com'),
  15. (5, 'Billie', 24, 'test5@baomidou.com');
  16. -- 真实开发中,version(乐观锁)、deleted(逻辑删除)、gmt_creategmt_modified
  1. 初始化 SpringBoot 项目
  2. 导入依赖
<!--    数据库驱动   -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
<!--   lombok     -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>
<!--   mybatis-plus     -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus</artifactId>   # 这里导错包了
    <version>3.0.5</version>
</dependency>

说明:使用 mybatis-plus 可以节省大量代码,尽量不要同时导入 mybatis 和 mybatis-plus

  1. 连接数据库,和 mybatis 相同
spring.datasource.username=root
spring.datasource.password=yuyihan
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?\
  useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

传统方式 pojo-dao(连接 mybatis,配置 mapper.xml 文件)-service-controller

  1. 使用 mybatis-plus 之后
  • pojo
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;

}
  • mapper
//在对应的Mapper上面继承基本的类BaseMapper
@Repository //代表持久层
public interface UserMapper extends BaseMapper<User> {
}
  • 在启动类上扫描 mapper 包下接口

    @MapperScan(“com.kuang.mapper”)

  • 测试
@SpringBootTest
class MybatisPlusApplicationTests {
    @Autowired
    private UserMapper userMapper;

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

}
  1. 结果

Mybatis快速入门 - 图2
方法&SQL 语句都由 Mybatis-Plus 写好了

Bug

Mybatis快速入门 - 图3
导错包了..上面第四步应该是 mybatis-plus-boot-starter 而不是 mybatis-plus

配置日志

当前 sql 不可见,需要知道如何执行

配置日志

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

Mybatis快速入门 - 图4

CRUD 扩展

插入测试

    @Test
    public void testInsert(){
        User user = new User();
        user.setName("易水寒");
        user.setAge(23);
        user.setEmail("ihan.com");
        int result = userMapper.insert(user);
        System.out.println(result);
    }

Mybatis快速入门 - 图5

数据库插入的 id 默认值:全局的唯一 id

主键生成策略

默认 ID_WORKER 全局唯一 id

分布式唯一 id 生成汇总方案
雪花算法:
snowflake 是 Twitter 开源的分布式 ID 生成算法,结果是一个 long 型的 ID。其核心思想是:使用 41bit 作为毫秒数,10bit 作为机器的 ID(5 个 bit 是数据中心,5 个 bit 的机器 ID),12bit 作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是 0。具体实现的代码可以参看https://github.com/twitter/snowflake。雪花算法支持的 TPS 可以达到 419 万左右(2^22*1000)。

主键自增

  1. 实体类字段上@TableId(type = IdType. ID_AUTO)
  2. 数据库配置自增 id

其他源码解释

public enum IdType {
    AUTO(0), // 数据库id自增
    NONE(1), // 未设置主键
    INPUT(2), // 手动输入
    ID_WORKER(3), // 默认的全局唯一id
    UUID(4), // 全局唯一id uuid
    ID_WORKER_STR(5); //ID_WORKER 字符串表示法
}

更新操作

    //测试更新
    @Test
    public void testUpdate(){
        User user = new User();
        user.setId(5L);
        user.setName("Bobby");
        userMapper.updateById(user);
    }

自动填充

创建时间、修改时间等操作都是自动化完成
阿里开发手册:gmt_created,gmt_modified

方式一:数据库级别(工作中不允许修改数据库)

  1. 在表中新增字段

Mybatis快速入门 - 图6

  1. 再次测试插入方法,先同步实体类
    private Date createTime;
    private Date updateTime;

Mybatis快速入门 - 图7

方式二:代码级别

  1. 删除数据库默认值、更新操作

Mybatis快速入门 - 图8

  1. 实体类字段添加注释
    //字段添加填充内容
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
  1. 编写处理器处理注解
@Slf4j
@Component  //一定不要忘记把处理器加到IOC容器中
public class MyMetaObjectHandler implements MetaObjectHandler {
    //插入时填充策略
    @Override
    public void insertFill(MetaObject metaObject) {
      log.info("start insert fill...");
      this.setFieldValByName("creatTime",new Date(),metaObject);
      this.setFieldValByName("updateTime",new Date(),metaObject);
    }
    //更新时填充策略
    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("start update fill...");
        this.setFieldValByName("updateTime",new Date(),metaObject);
    }
}
  1. 测试插入,测试更新,OK

乐观锁

面试中经常问到,其实很简单

乐观锁十分乐观,总是认为不会出现问题,无论干什么都不上锁,如果出现问题,再次更新值测试。
悲观锁十分悲观,总是认为会出现问题,无论感受那么都会上锁,再去操作。

version、new version
乐观锁机制:

  • 取出记录时,获取当前 version
  • 更新时,带上 version
  • 执行更新时,set version = newVersion where version = oldVersion
  • 如果 version 不对,更新失败
-- A线程
update user set name = "kuangshen", version = version + 1
where id = 2 and version = 1
-- B线程
update user set name = "kuangshen", version = version + 1
where id = 2 and version = 1

测试 MP 的乐观锁插件

  1. 数据库增加 version 字段

Mybatis快速入门 - 图9

  1. 实体类加对应字段
@Version
private Integer version;
  1. MybatisPlusConfig中注册乐观锁组件
@MapperScan("com.kuang.mapper")
@EnableTransactionManagement
@Configuration
public class MyBatisPlusConfig {
    //注册乐观锁插件
    @Bean
    public OptimisticLockerInterceptor optimisticLockerInterceptor() {
        return new OptimisticLockerInterceptor();
    }
}
  1. 测试
    //测试乐观锁成功
    @Test
    public void testOptimisticLocker(){
        User user = userMapper.selectById(1L);
        user.setName("ihan");
        user.setEmail("hhh.com");
        userMapper.updateById(user);
    }

    //测试乐观锁失败
    @Test
    public void testOptimisticLocker2(){
        //线程1
        User user = userMapper.selectById(1L);
        user.setName("ihan1");
        user.setEmail("qqq.com");
        //线程2
        User user2 = userMapper.selectById(1L);
        user2.setName("ihan2");
        user2.setEmail("lll.com");
        userMapper.updateById(user2);
        //如果没有乐观锁,就会覆盖插队线程的值
        userMapper.updateById(user);
    }

查询操作

    //测试查询
    @Test
    public void testSelectById(){
        User user = userMapper.selectById(1L);
        System.out.println(user);
    }
    //测试批量查询
    @Test
    public void testSelectByBatchId(){
        List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
        users.forEach(System.out::println);
    }
    //条件查询 map
    @Test
    public void testSelectByMap(){
        HashMap<String, Object> map = new HashMap<>();
        //自定义查询
        map.put("name","易水寒");
        List<User> users = userMapper.selectByMap(map);
        users.forEach(System.out::println);
    }

分页查询

  1. 原始的 limit 进行分页

  2. 第三方插件 pageHelper

  3. MP 内置分页插件

    使用

  1. 在 Config 中配置拦截器组件
// 分页插件
@Bean
public PaginationInterceptor paginationInterceptor() {
    return new PaginationInterceptor();
}
  1. 测试分页查询
    //测试分页查询
    @Test
    public void testPage(){
        Page<User> page = new Page<>(1, 5);
        userMapper.selectPage(page, null);
        page.getRecords().forEach(System.out::println);
    }

删除操作

    //测试删除
    @Test
    public void testDeleteByMap(){
        HashMap<String, Object> map = new HashMap<>();
        map.put("name","ihan");
        userMapper.deleteByMap(map);
    }

逻辑删除

物理删除:从数据库中直接移除
逻辑删除:通过变量让他失效 deleted = 0,1

管理员可以查看被删除的记录,防止数据丢失。

  1. 在数据库增加 delete 字段

Mybatis快速入门 - 图10

  1. 实体类中添加属性
@TableLogic
private Integer deleted;
  1. 在 Config 中配置逻辑删除组件
// 逻辑删除组件
@Bean
public ISqlInjector sqlInjector() {
    return new LogicSqlInjector();
}

application.properties中配置逻辑删除设置

# 配置逻辑删除
mybatis-plus.global-config.db-config.logic-delete-value=1
mybatis-plus.global-config.db-config.logic-not-delete-value=0
  1. 测试一下删除
    @Test
    public void testDeleteById(){
        userMapper.deleteById(1L);
    }

Mybatis快速入门 - 图11
本质进行了更新操作,数据库里还有数据,但查询语句已经查不到了

ps:高版本只需要添加注解即可,无需配置

性能分析插件

平时开发中遇到一些慢 sql,一般通过一些测试、压测工具揪出来。
MP 也提供了性能分析插件,如果超过规定时间,停止运行!

作用:分析每条 sql 执行的时间

  1. 在 Config 中配置插件
// SQL执行效率插件
@Bean
@Profile({"dev","test"})// 设置 dev test 环境开启,保证我们的效率
public PerformanceInterceptor performanceInterceptor() {
    PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
    //在工作中,不允许用户等待太久
    performanceInterceptor.setMaxTime(100); // mp设置sql执行的最大时间,如果超过了则不执行
    performanceInterceptor.setFormat(true); // 是否格式化代码
    return performanceInterceptor;
}

ps:3.1 以上版本需要引入 p6spy 依赖并配置!

application.properties中设置环境

# 设置开发环境
spring.profiles.active=dev
  1. 测试使用

重新执行查询 List 语句,超出规定时间就会抛出异常!
Mybatis快速入门 - 图12

条件构造器*

用来替代写一些复杂的单表查询 SQL 语句,多表用单表合并或手写 SQL
测试:记住查看输出的 SQL 进行分析

@Test void contextLoads() {
    // 查询name不为空的用户,并且邮箱不为空的用户,年龄大于等于12
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper .isNotNull("name") .isNotNull("email") .ge("age",12);
    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(){
    // 查询年龄在 20 ~ 30 岁之间的用户
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.between("age",20,30); // 区间
    Integer count = userMapper.selectCount(wrapper);// 查询结果数
    System.out.println(count);
}
// 模糊查询
@Test void test4(){ // 查询名字不含e,邮箱t打头
    QueryWrapper<User> wrapper = new QueryWrapper<>(); // 左和右 -> %t和t%
    wrapper .notLike("name","e") .likeRight("email","t");
    List<Map<String, Object>> maps = userMapper.selectMaps(wrapper);
    maps.forEach(System.out::println);
}
// 模糊查询
@Test void test5(){
    QueryWrapper<User> wrapper = new QueryWrapper<>(); // id 在子查询中查出来
    wrapper.inSql("id","select id from user where id<3"); // IN 查询
    List<Object> objects = userMapper.selectObjs(wrapper);
    objects.forEach(System.out::println);
}
//测试六
@Test void test6(){
    QueryWrapper<User> wrapper = new QueryWrapper<>(); // 通过id进行排序
    wrapper.orderByAsc("id");
    List<User> users = userMapper.selectList(wrapper);
    users.forEach(System.out::println);
}

代码生成器

官网:MybatisPlus-代码生成器(旧)

配置

// 代码自动生成器 public class KuangCode {
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/kuang_community? useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8");
    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("blog_tags","course","links","sys_settings","user_record"," user_say"); // 设置要映射的表名
    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(); //执行
}
}