mybatisPlus

寻技术 JAVA编程 / 其他编程 2023年09月03日 67

mybatisPlus

mybatisplus

基础: mybatis spring springmvc

为什么要学习mybatisplus ?

可以解决大量时间 所有的CRUD 代码它都可以自动化完成

简介

简化 jdbc 操作

简化 mybatis

快速入门

网站:快速开始 | MyBatis-Plus (baomidou.com)

使用第三方依赖

  1. 导入对应的依赖
  2. 研究依赖如何配置
  3. 代码如何编写
  4. 提高扩展技术能力

步骤

  1. 创建数据库 mybatis_plus

  2. 现有一张 User 表,其表结构如下:

    id name age email
    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

​ 其对应的数据库 Schema 脚本如下:

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

​ 其对应的数据库 Data 脚本如下:

DELETE FROM user;

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');
  1. 编写项目 ,初始化项目

    <!--数据库驱动-->   
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
    </dependency>
    <!-- lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
    <!-- mybatis-plus -->
    <!-- mybatis-plus 是自己开发的,并非官方的! -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.0.5</version>
    </dependency>
    
    

    说明

    ​ 使用mybatisplus 可以节省我们大量的代码,尽量不要同时导入 mybatis 和 mybatis-plus!

    ​ 版本差异

  2. 链接数据库

    spring.couchbase.username=root
    spring.datasource.password=123456
    spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf8
    # mysql8 serverTimezone=GMT%2B8
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    

    pojo-dao(链接mybatis,配置mapper.xml文件)-service-controller

  1. mapper 接口

    package com.lmq.mapper;
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.lmq.pojo.User;
    import org.springframework.stereotype.Repository;
    
    /**
     * @author 羡鱼
     * @version 1.0
     * @date 2023/7/20 20:23
     */
    
    @Repository  // 持久层
    public interface UserMapper extends BaseMapper<User> {
        // 所有CRUD操作都已经编写完成了
        // 你不需要配置一大堆文件了
    }
    
    

    注意: 我们需要在主启动类上去扫描我们的mapper包下的所有接口 @MapperScan("com.lmq.mapper")

    测试类

    package com.lmq;
    
    import com.lmq.mapper.UserMapper;
    import com.lmq.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);
    		users.forEach(System.out::println);
    	}
    
    }
    
    

    1、sql是谁帮我写的? MyBatis-plus 写好了

    2、方法哪里来的? MyBatis-plus 都写好了

配置日志

我们所以的sql现在是不可见的,我们希望知道是怎么执行的

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

img

CRUD扩展

insert

	@Test
	public void testInsert(){
		User user = new User();
		user.setName("lmq");
		user.setAge(21);
		user.setEmail("1435456124@qq.com");

		int result = userMapper.insert(user); // 帮我们自动生成id
		System.out.println(result); //受影响的行数
		System.out.println(user); // 发现id会自动回填
	}

img

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

主键生成策略

默认ID_WORKER 全局唯一ID

  1. 分布式系统唯一id 生成:终极版:分布式唯一ID的几种生成方案 - 简书 (jianshu.com)

雪花算法:

snowflake 是 Twitter 开源的分布式id生成算法,结果是个long型的id.核心思想是:使6用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在毫秒可以产生4096个ID),最后hiatus有一个符号位,永远是0,可以保证全球唯一

主键自增

我们需要配置主键自增

  1. 实体类字段上@TableId(type= Idtype.AUTO)
  2. 数据库字段也要是自增

其他源码解释

public enum IdType {
    AUTO(0),  // 数据库自增
    NONE(1),  // 未设置主键
    INPUT(2),  // 手动输入
    ID_WORKER(3),  // 默认 雪花算法 id
    UUID(4),         // 
    ID_WORKER_STR(5);  //字符串截取

    private int key;

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

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

更新操作

自动填充

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

  1. 表中加入字段 create_time update_time 默认值为CURRENT_TIMESTAMP

img

方式二:代码级别

  1. 删除数据库的默认值

  2. 实体类字段属性上需要增加注解

    // 字段添加填充内容
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
    
  3. 编写处理器来处理这个注解

    package com.lmq.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;
    
    /**
     * @author 羡鱼
     * @version 1.0
     * @date 2023/7/21 17:49
     */
    @Slf4j
    @Component  // 不要忘记把处理器加入到我们的ioc容器中
    public class MyMetaObjectHandler implements MetaObjectHandler {
        // 插入时候的填充策略
        @Override
        public void insertFill(MetaObject metaObject) {
            log.info("start insert fill 插入填充");
            // setFieldValByName(String fieldName, Object fieldVal, 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);
        }
    }
    

乐观锁 : 顾名思义 十分乐观 它认为不会出问题,无论干什么都不去上锁

悲观锁: 顾名思义十分悲观 他认为都会出问题 无论什么都去上锁

 `version` `new version`

乐观锁实现方式

  • 取出记录时,获取当前的version
  • 更新时,带上这个version
  • 执行时 set version = newVersion where version = oldVersion
  • 如果version不对,更新失败
     乐观锁 1 先查询 
     
     -- A
     update User set name = "lmq",version = version+1
     where id = 2 and version = 1
     -- B  线程抢线完成 这个时候 version = 2 会导致 A修改失败!
     update User set name = "lmq",version = version+1
     where id = 2 and version = 1

测试一下MP中的乐观锁插件

  1. 给数据库添加version字段

  2. 我们实体类加对应的字段

    // 乐观锁的version注解
    @Version
    private Integer version;
    
  3. 注册组件

    // Spring Boot 方式
    @Configuration
    @MapperScan("按需修改")
    public class MybatisPlusConfig {
        /**
         * 旧版
         */
        @Bean
        public OptimisticLockerInterceptor optimisticLockerInterceptor() {
            return new OptimisticLockerInterceptor();
        }
    
        /**
         * 新版
         */
        @Bean
        public MybatisPlusInterceptor mybatisPlusInterceptor() {
            MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
            mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
            return mybatisPlusInterceptor;
        }
    }
    
  4. 测试

    @Test
    public void test2OptimisticLocker(){
        // 线程1
        User user = userMapper.selectById(1l);
        user.setName("lmq");
        user.setEmail("1435456124@qq.com");
    
    
        // 模拟线程插队操作
        User user2 = userMapper.selectById(1l);
        user2.setName("lmq222");
        user2.setEmail("LLL1435456124@qq.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);
    }

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


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

分页查询

网站使用十分多

  1. 原始的limit进行分页
  2. pageHelper 第三方的插件
  3. MP内置了分页插件

如何使用

  1. 导入插件

        @Bean
        public PaginationInterceptor paginationInterceptor() {
    
            return new PaginationInterceptor();
        }
    
  2. 直接使用Page对象

        //测试分页查询
        @Test
        public void testPage(){
            // 参数一 当前页   参数二 页面大小  所有的分页变简单了
            Page<User> page = new Page<>(2,5);
    
            userMapper.selectPage(page,null);
            page.getRecords().forEach(System.out::println);
            System.out.println(page.getTotal());
        }
    

删除操作

    //测试删除
    @Test
    public void testDeleteById() {  // 单个id
        userMapper.deleteById(1682330740093681666L);
    }

    @Test
    public void testDeleteBatchIds() {  // 多个id
        userMapper.deleteBatchIds(Arrays.asList(1L, 2L));
    }

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

逻辑删除

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

逻辑删除: 在数据库中没有删除,而是通过一个变量来让他失效! deleted =0=>deleted =1

管理员可以看见删除记录! 防止数据丢失,类似回收站

  1. 在数据库中加入deleted 字段

  2. pojo 实体类中增加属性

        // 逻辑删除
        @TableLogic
        private Integer deleted;
    
  3. 配置bean

        @Bean
        public ISqlInjector sqlInjector() {
            return new LogicSqlInjector();
        }
    
  4. 配置properties

    # 配置逻辑删除
    mybatis-plus.global-config.db-config.logic-delete-value=1
    mybatis-plus.global-config.db-config.logic-not-delete-value=0
    

性能分析插件

开发中经常遇到慢sql 测试! druid..

MP也提供性能分析插件 超过时间就停止运行

  1. 导入插件

    @Bean
    @Profile({"dev", "test"}) //保证效率    设置环境
    public PerformanceInterceptor performanceInterceptor() {
        PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
        performanceInterceptor.setMaxTime(100); //sql执行的最大时间  如果超过就不仔细了
        performanceInterceptor.setFormat(true);
        return performanceInterceptor;
    
    }
    
    #设置开发环境
    spring.profiles.active=dev
    
  2. 测试使用

 Time:78 ms - ID:com.lmq.mapper.UserMapper.insert
Execute SQL:
    INSERT 
    INTO
        user
        ( id, name, age, email, create_time, update_time ) 
    VALUES
        ( 1682397170604474369, 'lmq', 21, '1435456124@qq.com', '2023-07-21 22:28:37.558', '2023-07-21 22:28:37.559' )

条件构造器

十分重要Wrapper

@Test
void contextLoads(){  //名字和邮箱不为空  年龄为不小于21的
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.isNotNull("name")
            .isNotNull("email")
            .ge("age","21");
    userMapper.selectList(wrapper).forEach(System.*out*::println);
}

@Test
void test2(){  //查询名字为Tom的
    QueryWrapper<User> wrapper = new QueryWrapper<>();
//        User user = userMapper.selectOne(wrapper);
    wrapper.eq("name","666");
    User user1 = userMapper.selectOne(wrapper);
    System.*out*.println(user1);
}

@Test
void test3(){  //查询20-30之间的
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.between("age","20","30");

    Integer integer = userMapper.selectCount(wrapper);
    System.*out*.println(integer);
}

@Test
void test4(){  //模糊查询
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.notLike("name","l")
    // 左和右这么区分的 %在左还是在右  e% 右
            .likeLeft("email","m");
    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<6");
    List<Object> objects = userMapper.selectObjs(wrapper);
    objects.forEach(System.*out*::println);
}

@Test
void test6(){
   QueryWrapper<User> wrapper = new QueryWrapper<>();
    // id 在子查询中出来的
    wrapper.orderByDesc("id");
   List<User> users = userMapper.selectList(wrapper);
    users.forEach(System.*out*::println);
}


代码自动生成器 [官网有新版本的]

dao pojo servlet controller

package com.lmq;

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

/**
 * @author 羡鱼
 * @version 1.0
 * @date 2023/7/22 16:36
 */
public class lmqCode {
    public static void main(String[] args) {
        // 构建代码生成器的对象
        AutoGenerator mpg = new AutoGenerator();

        //1. 全局配置
        GlobalConfig gc = new GlobalConfig();
        String propertyPath = System.getProperty("user.dir");
        // 代码生成的目录
        gc.setOutputDir(propertyPath + "/src/main/java");
        // 作者名字
        gc.setAuthor("羡鱼");
        // 打开文件夹
        gc.setOpen(false);
        // 是否覆盖原有的数据
        gc.setFileOverride(false);
        // 去掉Service 的I前缀
        gc.setServiceName("%sService");
        // 主键默认算法  //雪花算法  auot 自增 uuid 等
        gc.setIdType(IdType.ID_WORKER);
        // 日期类型
        gc.setDateType(DateType.ONLY_DATE);
        // 自动配置swagger文档
        gc.setSwagger2(true);

        // 配置全局配置到mpg里面
        mpg.setGlobalConfig(gc);
        // 2 设置数据原

        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf8");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("123456");
        dsc.setDbType(DbType.MYSQL);
        mpg.setDataSource(dsc);

        // 包的配置
        PackageConfig pc = new PackageConfig();
        // 模块名
        pc.setModuleName("bbs");
        // 生成的包父目录
        pc.setParent("com.lmq");
        // 详细的分层的包
        pc.setEntity("entity");
        pc.setMapper("mapper");
        pc.setService("service");
        pc.setController("controller");

        mpg.setPackageInfo(pc);
        // 4. 策略配置 乐观锁等
        StrategyConfig strategy = new StrategyConfig();
        // 设置要映射的表
        strategy.setInclude("user");
        strategy.setNaming(NamingStrategy.underline_to_camel);

        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        strategy.setSuperEntityClass("你自己的父类实体,没有就不用设置!");
//        是否使用lombok
        strategy.setEntityLombokModel(true);
        // 逻辑删除
        strategy.setLogicDeleteFieldName("deleted");

        strategy.setRestControllerStyle(true);
        // 自动填充操作   字段名 + 操作
        TableFill gmtCreate = new TableFill("gmt_create", FieldFill.INSERT);
        TableFill gmtCreate2 = new TableFill("gmt_modified", FieldFill.UPDATE);
        ArrayList<TableFill> tables = new ArrayList<>();
        tables.add(gmtCreate);
        tables.add(gmtCreate2);
        strategy.setTableFillList(tables);
        // 乐观锁
        strategy.setVersionFieldName("version");
        // 驼峰命名
        strategy.setRestControllerStyle(true);
        // 下划线命名   localhost:8080/hello_id_2
        strategy.setControllerMappingHyphenStyle(true);
        mpg.setStrategy(strategy);

        //执行
        mpg.execute();
    }
}

关闭

用微信“扫一扫”