2

对狂神说的MybatisPlus的学习总结

 3 years ago
source link: https://www.cnblogs.com/feng-zhi/p/14846560.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

1.什么是MybatisPlus?

需要的基础:spring,spring mvc,mybatis
作用:可以节省大量的工作时间,所有的CRUD代码都可以自动完成,简化Mybatis
MyBatis-Plus (opens new window)(简称 MP)是一个 MyBatis (opens new window) 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

MybatisPlus官网

  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
  • 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
  • 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
  • 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
  • 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
  • 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
  • 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
  • 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用(自动帮你生成代码 )
  • 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
  • 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
  • 内置性能分析插件:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
  • 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作

2.快速入门

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

根据官方文档开始入门
快速开始

2.1创建数据库(我创建的就是命名MybatisPlus数据库)

2.2创建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)
);

真实开发中这些字段也是必不可少的,version(乐观锁),deleted(逻辑删除),gmt_create,gmt_modified

插入数据

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

2.3编写项目,初始化项目,初始化一个springboot项目

2.4导入依赖

<!--        数据库驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
<!--mybatis_plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.0.7</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>

2.5 连接数据库

#mysql  5
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

# mysql 8 驱动不同 com.mysql.cj.jdbc.Driver . 需要增加时区的配置 serverTimezone=GMT%2B8

2.6使用mybatis_plus

  • mapper接口

User类的编写

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

UserMapper的编写

//在对应的Mapper上面实现基本的接口BaseMapper
@Repository//代表持久层
public interface UserMapper extends BaseMapper<User> {
//    所有的CRUD操作编写完成
//    不需要和原来一样写一大堆的文件了
}

在应用添加mapperscan扫描

@SpringBootApplication
@MapperScan("cn.dzp.mapper")
public class MybatisPlusApplication {

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

}

测试类编写

@Autowired
    private UserMapper userMapper;
    @Test
    void contextLoads() {
//        参数是Wrapper,条件构造器,暂时先不用
        List<User> users = userMapper.selectList(null);
        users.forEach(System.out::println);
//        for (User user : users) {
//            System.out.println(user);
//        }
    }

查询所有用户

3.配置日志

我们所有的sql现在是不可见的,我们希望知道它是这么执行的,所以我们必须要看日志

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

配置完成日志后,后面的学习就需要注意这个自动生成的sql,就会喜欢上myabatis-plus!

4.CRUD基本操作

4.1插入功能

编写测试代码

    @Test
    public void testInsert(){
        User user = new User();
        user.setName("邓疯子");
        user.setAge(20);
        user.setEmail("[email protected]");

        int insert = userMapper.insert(user);
        System.out.println(insert);
        System.out.println(user);
    }

运行查看发现我并没有设置ID,但是它帮我自动生成了ID,现在来研究下自动生成ID

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

4.2主键生成策略

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

雪花算法在工程实现上有单机版本和分布式版本。单机版本如下,分布式版本可以参看美团leaf算法:https://github.com/Meituan-Dianping/Leaf

  • redis
  • zookeeper

主键自增
1.实体类上

@TableId(type = IdType.AUTO)
    private Long id;

其余的源码解释

    AUTO(0),//自增
    NONE(1),//未设置主键
    INPUT(2),//手动输入,需要自己配置id
    ID_WORKER(3),//默认的全局唯一id
    UUID(4),//全球唯一id
    ID_WORKER_STR(5);//ID_WORKER字符串表示

2.数据库字段一定要自增

在进行插入测试,完成自增

4.3 测试更新

编写测试代码

 @Test
    public void testUpdate(){
        User user = new User();
        user.setId(1400650930251329539L);
        user.setName("邓疯子很菜");
        int i = userMapper.updateById(user);
        System.out.println(i);
    }

所有的sql都是自动帮你动态配置的

4.4自动填充

创建时间,修改时间,这些操作一般都是自动化完成的,不希望手动更新
比如数据表:gmt_create,gmt_modified,几乎所有的表都要配置上,便于追踪

方式一:数据库级别(工作中不允许你修改数据库的)
1.在表中新增字段create_time,update_time,两个字段都需要current_timestamp更新

2.再次测试插入方法,需要先把实体类同步

    private Date createTime;
    private Date updateTime;

可以看到已经更新了,时间也自动更新

方式二:代码级别
1.删除数据库的默认值,更新操作

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

//    字段添加填充内容
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;

    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;

3.编写处理器处理这个注解即可!

package cn.dzp.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;

@Component
@Slf4j
 public class MyMetaObjectHandler implements MetaObjectHandler {
//    插入时候的填充策略
    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("start insert fill......");
//        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);
    }
}

查看数据库

查看数据库,只更新了更新的时间

4.5 乐观锁

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

这里主要讲解:乐观锁机制
乐观锁实现方式:

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

测试一下MP(MybatisPlus简写)的乐观锁插件

1.给数据库加上version字段

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

@Version//代表这是一个乐观锁的注解
   private Integer version;

3.注册组件

package cn.dzp.config;

import com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@MapperScan("cn.dzp.mapper")
@EnableTransactionManagement
@Configuration//配置类
public class MybatisPlusConfig {
//    注册乐观锁插件
    @Bean
    public OptimisticLockerInterceptor optimisticLockerInterceptor(){
        return new OptimisticLockerInterceptor();
    }
}

4.测试乐观锁成功

//    测试乐观锁成功
    @Test
    public void testOptimisticLocker(){
//        1.查询用户信息
        User user = userMapper.selectById(1L);
//        2.修改用户信息
        user.setName("乐观锁");
        user.setEmail("leguansuo.com");
//        3.更新操作
        userMapper.updateById(user);
    }

5.测试乐观锁失败

//    测试乐观锁失败!多线程下
@Test
public void testOptimisticLocker2(){
//        线程1
    User user1 = userMapper.selectById(1L);
    user1.setName("乐观锁111");
    user1.setEmail("leguansuo111.com");

//    模拟线程2执行了插队操作
    User user2 = userMapper.selectById(1L);
    user2.setName("乐观锁222");
    user2.setEmail("leguansuo222.com");
    userMapper.updateById(user2);


    userMapper.updateById(user1);//没有乐观锁就会覆盖插队线程的值
}

4.6查询操作

//测试查询
    @Test
    public void testSelectById(){
        User user = userMapper.selectById(1L);
        System.out.println(user);
    }
//    测试批量查询
    @Test
    public void testSelectBatchId(){
        List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3, 4, 5));
        users.forEach(System.out::println);
    }
    @Test
//    条件查询之一 map
    public void testSelectByBatchIds(){
        HashMap<String, Object> map = new HashMap<>();
//        自定义查询
        map.put("name","邓疯子");
        List<User> users = userMapper.selectByMap(map);
        users.forEach(System.out::println);
    }

4.7 分页查询

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

如何使用
1.配置拦截器组件即可

// 旧版分页插件
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        return new PaginationInterceptor();
    }

2.测试分页查询

//    测试分页查询
    @Test
    public void testPage(){
//        参数一:当前页
//        参数二:页面大小
        Page<User> page = new Page<>(1,5);
        userMapper.selectPage(page,null);

        page.getRecords().forEach(System.out::println);
    }

4.8 逻辑删除

物理删除:从数据库中直接移除
逻辑删除:没有从数据库中移除,而是通过一个变量来让它失效!deleted = 0 ==> deleted = 1

测试:
1.在数据库表中增加一个deleted字段

2.实体类中增加属性

    @TableLogic//逻辑删除
    private Integer deleted;
//注册逻辑删除
    @Bean
    public ISqlInjector sqlInjector(){
        return new LogicSqlInjector();
    }

配置文件配置

配置逻辑删除

mybatis-plus.global-config.db-config.logic-delete-value=1
mybatis-plus.global-config.db-config.logic-not-delete-value=0

4.测试删除
虽然走的是删除操作,但看日志文件可以发现是一个更新的操作

数据库里依旧存在,但是deleted值变化成1

试试能不能查询出来刚刚删除的用户

根据图展示,并没有查询到此用户,并且查询动态拼接了一个条件(deleted=0)

以上CRUD操作及其扩展操作,我们必须精通掌握!会大大提高你的工作和写项目的效率的!

5.性能分析插件

我们在平时的开发中,会遇到一些慢sql.

MP也提供了性能分析插件,如果超过这个时间就停止运行!
1.导入插件
(记得到配置文件里配置dev环境或者test环境)

#设置开发环境
spring.profiles.active=dev
@Bean
    @Profile({"dev","test"}) //设置dev 和 test环境开启,保证效率
    public PerformanceInterceptor performanceInterceptor(){
        PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
        performanceInterceptor.setMaxTime(100);//ms 设置sql执行的最大时间,如果超过了则不执行
        performanceInterceptor.setFormat(true);//是否格式化
        return performanceInterceptor;
    }

2.测试查询

 @Test
    void contextLoads() {
//        参数是Wrapper,条件构造器,暂时先不用
        List<User> users = userMapper.selectList(null);
        users.forEach(System.out::println);
//        for (User user : users) {
//            System.out.println(user);
//        }
    }

可以看到执行时间没有超过我们设置的100ms,所以执行成功

使用性能分析插件 ,可以帮助我们提高效率!

6.条件构造器

十分重要:Wrapper

我们写一些复杂的sql就可以用它来进行替代

编写新的测试类WrapperTest

package cn.dzp;

import cn.dzp.mapper.UserMapper;
import cn.dzp.pojo.User;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
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
public class WrapperTest {

    @Autowired
    private UserMapper userMapper;
    @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);
    }

    @Test
    void test2(){
        //查询名字等于邓疯子
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("name","邓疯子");
        System.out.println(userMapper.selectOne(wrapper));//查询一个数据,查询多个使用map或者list
    }

    @Test
    void test3(){
//        查询年龄在20-30之间的用户
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.between("age",20,30);
        System.out.println(userMapper.selectCount(wrapper));
    }

    @Test
    void test4(){
//        模糊查询
        QueryWrapper<User> wrapper = new QueryWrapper<>();
//        左和右  %test和test%
        wrapper
                .notLike("name","邓")
                .likeRight("email","test")
        ;
        userMapper.selectMaps(wrapper).forEach(System.out::println);
    }

    @Test
    void test5(){
        QueryWrapper<User> wrapper = new QueryWrapper<>();
//        id在子查询中查出来
        wrapper.inSql("id","select id from user where id<3 ");

        userMapper.selectObjs(wrapper).forEach(System.out::println);
    }
//测试6
    @Test
    void tesy6(){
//通过id进行排序
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.orderByDesc("id");
        userMapper.selectList(wrapper).forEach(System.out::println);
    }
}

7.代码自动生成器(非常强大)

dao、pojo、service、controller都给我自己去编写完成!
AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Entity、
Mapper、Mapper XML、Service、Controller 等各个模块的代码,极大的提升了开发效率。

7.1 添加依赖

MyBatis-Plus 从 3.0.3 之后移除了代码生成器与模板引擎的默认依赖,需要手动添加相关依赖:

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.0.7</version>
</dependency>

7.2 添加依赖添加 模板引擎 依赖,MyBatis-Plus 支持 Velocity(默认)、Freemarker、Beetl,用户可以选择自己熟悉的模板引擎,如果都不满足您的要求,可以采用自定义模板引擎。

        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-engine-core</artifactId>
            <version>2.3</version>
        </dependency>

7.3编写代码自动生成器类

package cn.dzp;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
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;

// 代码自动生成器
public class FengCode {
    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);//新版本ID_WORKER过时了,3.3.0后用ASSIGN_ID
        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");
        dsc.setDriverName("com.mysql.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("123456");
        dsc.setDbType(DbType.MYSQL);
        mpg.setDataSource(dsc);

//        3.包的配置
        PackageConfig pc = new PackageConfig();
        pc.setModuleName("AutoGenerator");
        pc.setParent("cn.dzp");
        pc.setEntity("pojo");
        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_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();//执行
    }
}

运行查看结果,可以看到我们设置的文件夹都被自动生成了,成功自动生成

但是可以看到实体类user中报红,这是因为我们设置了 gc.setSwagger2(true),所以需要Swagger2的依赖,但是pom没有导入依赖,所以报错,导入下依赖就好

对应依赖如下

<!-- https://mvnrepository.com/artifact/io.swagger/swagger-annotations -->
        <dependency>
            <groupId>io.swagger</groupId>
            <artifactId>swagger-annotations</artifactId>
            <version>1.6.2</version>
        </dependency>

在查看项目发现报红消失,问题完美解决

至此代码自动生成器部分完结

大家喜欢的可以去B站给狂神哥点波关注或者三连,他的java个人感觉讲的非常好滴!

本人关于狂神的MybatisPlus的总结与学习到这结束了

看到最后的大佬们帮忙点个推荐,谢谢,这个对我真的很重要!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK