• 一篇搞定MyBatisPlus!


    前言:该文章是以狂神说MyBatisPlus为参考,用以复习!初学者同样可以在这篇文章学到东西!
    B站:狂神说MyBatisPlus地址:https://www.bilibili.com/video/BV17E411N7KN
    狂神牛逼,强烈安利狂神!

    配置环境

    导入依赖:

    <dependencies>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starterartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>
        
        
        <dependency>
            <groupId>com.baomidougroupId>
            <artifactId>mybatis-plus-boot-starterartifactId>
            <version>Latest Versionversion>
        dependency>
        
        
        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <version>8.0.16version>
        dependency>
        
        
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <version>1.18.12version>
            <scope>providedscope>
        dependency>
    dependencies>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    1. 配置数据库等

      yaml:

    #数据库配置
    spring:
      datasource:
        username: root
        password: root
        url: jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8
        driver-class-name: com.mysql.cj.jdbc.Driver
    #日志配置
    mybatis-plus:
      configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    properties:

    # 数据库配置
    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/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8
    
    # 日志配置 (默认控制台输出)
    mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    ★CRUD扩展

    1.插入数据

    @Test
    public void testInsert(){
        User user = new User();
        user.setName("codeyuaiiao");
        user.setAge(3);
        user.setEmail("747557612@qq.com");
        int result = userMapper.insert(user);
        System.out.println(result);
        System.out.println(user);
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    注意:插入的Id要是全局唯一Id

    2.主键自增策略

    默认 ID_WORKER 全局唯一id

    对应数据库中的主键(uuid.自增id.雪花算法.redis.zookeeper)

    ★雪花算法(Twitter的snowflake算法)

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

    ★主键自增

    我们需要配置主键自增:

    1. 实体类字段上@TableId(type = IdType.AUTO)

    在这里插入图片描述

    1. 数据库字段一定要是自增! 数据库和实体类一定要对应!!!

    在这里插入图片描述

    3.其余的自增方式解读:

    public enum IdType {
         
        AUTO(0),//数据库ID自增  
        NONE(1),//该类型为未设置主键类型      
        INPUT(2),//用户输入ID
          		 //该类型可以通过自己注册自动填充插件进行填充  
    
    //以下3种类型、只有当插入对象ID 为空,才自动填充。     
        ID_WORKER(3),//全局唯一ID (idWorker)      
        UUID(4),//全局唯一ID (UUID)          
        ID_WORKER_STR(5);//字符串全局唯一ID (idWorker 的字符串表示)    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    2.更新数据

    在这里插入图片描述

    注意这里的update的参数都是一个实体类,并不是Id!

    @Test
    public void testUpdate(){
        User user = new User();
        user.setId(2L);
        user.setEmail("32134@qq.com");
        user.setName("CJ");
        int update = userMapper.updateById(user);
        System.out.println(update);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述

    传入的是user,但发现它确实是按照Id来更新数据的!

    ★3.自动填充

    创建时间 . 修改时间! 这些个操作都是自动化完成的,我们不希望手动更新!

    阿里巴巴开发手册:所有的数据库表:gmt_create .gmt_modified几乎所有的表都要配置上!而且需要自动化!

    方式一:数据库级别

    不推荐使用!因为要修改数据库!

    1.在表中新增字段 create_time , update_time

    在这里插入图片描述

    在这里插入图片描述

    2.实体类要对应上

    在这里插入图片描述

    重新新增,时间出来了!

    方式二:代码级别

    1.删除数据库默认值
    在这里插入图片描述

    2.实体类增加注解

    @TableField(fill = FieldFill.INSERT)   //在插入时自动执行填充创建时间
    private Date createTime;
    
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
    
    • 1
    • 2
    • 3
    • 4
    • 5

    3.处理器处理注解

    @Slf4j    //打印一下日志  
    @Component      //注入到IOC容器中,别忘了!!!
    public class MyMetaObjectHandler implements MetaObjectHandler {
        
        @Override
        public void insertFill(MetaObject metaObject) {
            log.info("start insert fill...");
    //        default MetaObjectHandler 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) {
            log.info("start update fill...");
            this.setFieldValByName("updateTime",new Date(),metaObject);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    新增测试,成功!

    ★4.乐观锁

    乐观锁就是什么时候都认为不会出问题,干啥都不加锁,等出了问题再次更新值测试

    悲观锁就是认为啥时候都会出问题,无论干啥都会上锁,然后操作

    MP乐观锁实现

    如下:

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

    1.添加version字段
    在这里插入图片描述

    2.同步实体类

    //乐观锁注解
    @Version
    private Integer version;
    
    • 1
    • 2
    • 3

    3.注册组件 创建config包

    // 这个扫描本来是在我们MybatisPlusApplication 主启动类中,现在我们把它放在配置类中
    // 扫描mapper文件夹
    @MapperScan("com.chen.mapper")
    @EnableTransactionManagement  // 自动开启事务管理
    @Configuration // 配置类
    public class MybatisPlusConfig {
    
        //新版乐观锁插件
        @Bean
        public MybatisPlusInterceptor mybatisPlusInterceptor() {
            MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
            mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
            return mybatisPlusInterceptor;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    4.测试一下单线程中的乐观锁是否生效

    // 测试乐观锁单线程成功
    @Test
    public void testOptimisticLock(){
        // 1、查询用户信息
        User user = userMapper.selectById(1);
        // 2、修改用户信息
        user.setName("xiaoshaung");
        user.setEmail("123123132@qq.com");
        // 3、执行更新操作
        userMapper.updateById(user);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    看一下SQL语句:

    在这里插入图片描述

    确实添加有version这个条件!

    5.测试一下多线程的乐观锁

     /*
        线程1 虽然执行了赋值语句,但是还没进行更新操作,线程2就插队了抢先更新了,
        由于并发下,可能导致线程1执行不成功
        如果没有乐观锁就会覆盖线程2的值
     */
     // 测试乐观锁多线程失败
    @Test
    public void testOptimisticLock2(){
        // 线程1
        User user = userMapper.selectById(1);
        user.setName("cangjian111");
        user.setEmail("32143455@qq.com");
    
        // 模拟另外一个线程执行了插队操作
        // 线程2
        User user2 = userMapper.selectById(1);
        user2.setName("cagnjian222");
        user2.setEmail("32143455@qq.com");
        userMapper.updateById(user2);
    
        // 自旋锁来多次尝试提交
        userMapper.updateById(user);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    在这里插入图片描述

    111被吞掉!

    5.查询数据

    测试查询有如下方法:

    在这里插入图片描述

    挨个测试一把!

    //test select
    @Test
    public void testSelect(){
        User user = userMapper.selectById(2L);
        System.out.println(user);
    }
    
    //测试批量查询
    @Test
    public void testSelectBatchIds(){
        List<User> users = userMapper.selectBatchIds(Arrays.asList(1,2,3,4));
        users.forEach(System.out::println);
    }
    
    
    //测试按条件查询
    @Test
    public void testSelectMap(){
        Map<String, Object> map = new HashMap<>();
        map.put("name","cangbaobao");
        map.put("age",6);
        //List selectByMap(@Param(Constants.COLUMN_MAP) Map columnMap);
        List<User> users = userMapper.selectByMap(map);
        users.forEach(System.out::println);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    在这里插入图片描述

    ★6.逻辑删除

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

    逻辑删除:实际上没有真正删除数据,数据还存在!只是用了一个字段来区分,在每次查询的时候都加上这个字段的where判断,就能达到一个逻辑删除的效果! 回收站就是用了逻辑删除实现的。

    逻辑删除的步骤:

    1,增加数据库字段!

    在这里插入图片描述

    2.实体类同步!

    // 逻辑删除注解
    @TableLogic
    private Integer deleteId;
    
    • 1
    • 2
    • 3

    3.配置逻辑是删除组件!

    // 逻辑删除组件   高版本无序写这个配置   已经自动集成!
    @Bean
    public ISqlInjector iSqlInjector(){
        return new LogicSqlInjector();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    4.测试一下删除,发现有如下方法

    在这里插入图片描述

    这里直接用deleteById测试:

    //test delete
    @Test
    public void testDelete(){
        userMapper.deleteById(6L);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    发现:它竟然走的是update操作!

    在这里插入图片描述

    在这里插入图片描述

    之后正常的查询将无法查询到它,这就是逻辑查询!!!

    7.分页查询

    Mybatis-Plus也内置了分页插件!

    1.集成分页插件:

    // 最新版
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2));
        return interceptor;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2.可以用了:

        @Test
        public void testPage(){
    //        > E selectPage(E page, @Param(Constants.WRAPPER) Wrapper queryWrapper);
            Page<User> page = new Page<>(1,5);
            userMapper.selectPage(page, null);
    
            page.getRecords().forEach(System.out::println);
            System.out.println("getCurrent()"+page.getCurrent());
            System.out.println("page.getSize()"+page.getSize());
            System.out.println("page.getTotal()"+page.getTotal());
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    3.在这里插入图片描述

    实际上调用的也是limit!

    性能分析插件

    ★★条件构造器★★

    一些重要的方法:

    eq :

    • 等于 =
    • 例: eq("name", "老王")—>name = '老王'

    ne :

    • 不等于 <>
    • 例: ne("name", "老王")—>name <> '老王'

    gt :

    • 大于 >
    • 例: gt("age", 18)—>age > 18

    ge :

    • 大于等于 >=
    • 例: ge("age", 18)—>age >= 18

    lt :

    • 小于 <
    • 例: lt("age", 18)—>age < 18

    le :

    • 小于等于 <=
    • 例: le("age", 18)—>age <= 18

    between :

    • BETWEEN 值1 AND 值2
    • 例: between("age", 18, 30)—>age between 18 and 30

    not between :

    • NOT BETWEEN 值1 AND 值2
    • 例: notBetween("age", 18, 30)—>age not between 18 and 30

    like :

    • LIKE ‘%值%’
    • 例: like("name", "王")—>name like '%王%'

    notLike :

    • NOT LIKE ‘%值%’
    • 例: notLike("name", "王")—>name not like '%王%'

    likeLeft :

    • LIKE ‘%值’
    • 例: likeLeft("name", "王")—>name like '%王'

    likeRight :

    • LIKE ‘值%’
    • 例: likeRight("name", "王")—>name like '王%'

    isNull :

    • 字段 IS NULL
    • 例: isNull("name")—>name is null

    isNotNull :

    • 字段 IS NOT NULL
    • 例: isNotNull("name")—>name is not null

    还有好多,可以在官网上找到,写得都很详细!!!

    一些测试:

    @Test
    public void test1(){
        // 查询 name 不为空的用户,并且邮箱不为空的用户,年龄大于等于20
        //控制台查询的语句:SELECT id,age,email,name,version,delete_id,create_time,update_time FROM user WHERE delete_id=0 AND (name IS NOT NULL AND email IS NOT NULL AND age >= ?)
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.isNotNull("name")
                    .isNotNull("email")
                    .ge("age",20);
        userMapper.selectList(queryWrapper).forEach(System.out::println);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
        @Test
        public void test2(){
            // 查询名字 CJ
    //生成的SQL:SELECT id,age,email,name,version,delete_id,create_time,update_time FROM user WHERE delete_id=0 AND (name = ?)
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("name","CJ");
            User user = userMapper.selectOne(queryWrapper);
            System.out.println(user);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
        @Test
        public void test3(){
            //查询年龄在21到30的
    //        sql : SELECT id,age,email,name,version,delete_id,create_time,update_time FROM user WHERE delete_id=0 AND (age BETWEEN ? AND ?)
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            queryWrapper.between("age",21,30);
            userMapper.selectList(queryWrapper).forEach(System.out::println);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
        @Test
        public void test4(){
            //模糊查询  查询名字中含有i的和email以3开头的
    //        sql :SELECT id,age,email,name,version,delete_id,create_time,update_time FROM user WHERE delete_id=0 AND (name LIKE %i% AND email LIKE 3%)
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            queryWrapper.like("name","i")
                        .likeRight("email","3");
            userMapper.selectMaps(queryWrapper).forEach(System.out::println);
        }
    // userMapper.selectList(queryWrapper).forEach(System.out::println);  selectList和selectMap的区别就是返回值形式的不同,一个是按照user表中属性顺序来,一个是安照map形式显示
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
        @Test
        public void test5(){
            //连接查询(内查询)  查找id<6的
    //        sql :SELECT id,age,email,name,version,delete_id,create_time,update_time FROM user WHERE delete_id=0 AND (id IN (select id from user where id < 6))
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            queryWrapper.inSql("id","select id from user where id < 6");
            userMapper.selectList(queryWrapper).forEach(System.out::println);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    @Test
    public void test6(){
        //根据id倒叙排列
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByDesc("id");
        userMapper.selectList(queryWrapper).forEach(System.out::println);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    代码生成器

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

    有两个版本:3.5.1以下和以上

    我用的MP是3.4.2的,所以采用历史版本:

    1.导入依赖:

            
            <dependency>
                <groupId>com.baomidougroupId>
                <artifactId>mybatis-plus-generatorartifactId>
                <version>3.4.1version>
            dependency>
            <dependency>
                <groupId>org.apache.velocitygroupId>
                <artifactId>velocity-engine-coreartifactId>
                <version>2.3version>
            dependency>
            
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    2.编写一个自动生成类:注意包不要导错了 基本都是generator下的包

    // 代码自动生成器
    public class AutoGeneratorTest {
        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("CJ");
            gc.setOpen(false);
            gc.setFileOverride(false);  // 是否覆盖
            gc.setServiceName("%Service"); // 服务接口,去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/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2b8");
            dsc.setDriverName("com.mysql.cj.jdbc.Driver");
            dsc.setUsername("root");
            dsc.setPassword("root");
            dsc.setDbType(DbType.MYSQL);
            mpg.setDataSource(dsc);
    
            // 3、包的配置
    
            PackageConfig pc = new PackageConfig();
            pc.setModuleName("blog");
            pc.setParent("com.chen");
            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.setEntityLombokModel(true);  // 自动Lombok
    
            strategy.setLogicDeleteFieldName("deleted");  // 逻辑删除字段
    
            // 自动填充策略
            TableFill gmtCreate = new TableFill("gmt_create", FieldFill.INSERT);
            TableFill gmtModified = new TableFill("gmt_modified", FieldFill.INSERT);
    
            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();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73

    3.执行!
    在这里插入图片描述

    大功告成!!!

    MP完结!!!!

  • 相关阅读:
    运筹学基础【三】 之 决策
    生成数字图像基本过程
    40多行实现一个非常简单的shell
    数字电子技术笔记——组合逻辑功能
    利用Power Automate,轻松下载Power BI报告中的数据
    没想到这些软件也可以做到发票识别
    机器学习 - DBSCAN聚类算法:技术与实战全解析
    性能测试学习-基本使用-元件组件介绍(二)
    Spring Cloud Netflix之OpenFeign
    2022年湖北劳务资质办理需要准备什么资料?办理劳务需要注意哪些呢?甘建二
  • 原文地址:https://blog.csdn.net/qq_45830289/article/details/126218673