风在路上 风在路上
首页
导航站
  • Java-Se

    • Java基础
  • Java-Se进阶-多线程

    • 多线程
  • Java-Se进阶-java8新特性

    • java8新特性
  • Java-ee

    • JavaWeb
  • Java虚拟机

    • JVM
  • golang基础

    • golang基础
  • golang框架

    • gin
  • SQL 数据库

    • MySQL
  • NoSQL 数据库

    • Redis
    • ElasticSearch
    • MongoDB
  • ORM

    • MyBatis
    • MyBatis-Plus
  • Spring

    • Spring
  • SpringMVC

    • SpringMVC1
    • SpringMVC2
  • SpringCloud

    • SpringCloud
  • 中间件

    • RabbitMQ
    • Dubbo
  • 秒杀项目
  • Git
  • Linux
  • Docker
  • JWT
  • 面试
  • 刷题
开发问题😈
设计模式
关于💕
归档🕛
GitHub (opens new window)

风

摸鱼
首页
导航站
  • Java-Se

    • Java基础
  • Java-Se进阶-多线程

    • 多线程
  • Java-Se进阶-java8新特性

    • java8新特性
  • Java-ee

    • JavaWeb
  • Java虚拟机

    • JVM
  • golang基础

    • golang基础
  • golang框架

    • gin
  • SQL 数据库

    • MySQL
  • NoSQL 数据库

    • Redis
    • ElasticSearch
    • MongoDB
  • ORM

    • MyBatis
    • MyBatis-Plus
  • Spring

    • Spring
  • SpringMVC

    • SpringMVC1
    • SpringMVC2
  • SpringCloud

    • SpringCloud
  • 中间件

    • RabbitMQ
    • Dubbo
  • 秒杀项目
  • Git
  • Linux
  • Docker
  • JWT
  • 面试
  • 刷题
开发问题😈
设计模式
关于💕
归档🕛
GitHub (opens new window)
  • mybatis

  • mybatis-plus

    • mybatis-plus
    • Spring

    • SpringMvc

    • RabbitMQ

    • Dubbo

    • SpringCloud

    • 框架
    • mybatis-plus
    zdk
    2022-01-06
    目录

    mybatis-plus

    Table of Contents generated with DocToc (opens new window)

    • 快速开始
    • 配置日志输出
    • CRUD扩展
    • 乐观锁、悲观锁:
    • 基本查询操作
    • 删除操作
    • 逻辑删除
    • 条件构造器
    • 代码生成器

    特性

    • 无侵入∶只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑

    • 损耗小:启动即会自动注入基本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 操作智能分析阻断,也可自定义拦截规则,预防误操作

    支持数据库 mysql 、mariadb、oracle 、db2、h2 、hsql、sqlite 、postgresql、sqlserver、达梦数据库、虚谷数据库、人大金仓数据库

    # 快速开始

    • 新建springboot项目

    • 导入依赖

      <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-web</artifactId>
              </dependency>
      
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-devtools</artifactId>
                  <scope>runtime</scope>
                  <optional>true</optional>
              </dependency>
              <dependency>
                  <groupId>mysql</groupId>
                  <artifactId>mysql-connector-java</artifactId>
                  <scope>runtime</scope>
              </dependency>
              <dependency>
                  <groupId>org.projectlombok</groupId>
                  <artifactId>lombok</artifactId>
                  <optional>true</optional>
              </dependency>
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-test</artifactId>
              </dependency>
      <!--        mybatis-plus依赖-->
              <dependency>
                  <groupId>com.baomidou</groupId>
                  <artifactId>mybatis-plus-boot-starter</artifactId>
                  <version>3.4.3.1</version>
              </dependency>
      
      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
    • 编辑数据库配置文件

      spring:
        datasource:
          username: root
          password: root
          url: jdbc:mysql://localhost:3306/study?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=utf8
          driver-class-name: com.mysql.cj.jdbc.Driver
      
      1
      2
      3
      4
      5
      6
    • 导入数据表和测试数据

      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)
      );
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      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
      2
      3
      4
      5
      6
      7
      8
    • 实体类

      @Data
      @AllArgsConstructor
      @NoArgsConstructor
      public class User {
          private Long id;
          private String name;
          private Integer age;
          private String email;
      }
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
    • mapper

      @Repository
      public interface UserMapper extends BaseMapper<User> {
      
      }
      
      1
      2
      3
      4
    • 启动类扫描mapper包

      @SpringBootApplication
      @MapperScan("com.zdk.mapper")
      public class MybatisPlusStudyApplication {
          public static void main(String[] args) {
              SpringApplication.run(MybatisPlusStudyApplication.class, args);
          }
      }
      
      1
      2
      3
      4
      5
      6
      7
    • 测试

      @SpringBootTest
      class MybatisPlusStudyApplicationTests {
      
          @Autowired
          private UserMapper userMapper;
      
          @Test
          void contextLoads() {
              List<User> userList = userMapper.selectList(null);
              userList.forEach(System.out::println);
          }
      }
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12

      结果:

      user = User(id=1, name=Jone, age=18, email=test1@baomidou.com)
      user = User(id=2, name=Jack, age=20, email=test2@baomidou.com)
      user = User(id=3, name=Tom, age=28, email=test3@baomidou.com)
      user = User(id=4, name=Sandy, age=21, email=test4@baomidou.com)
      user = User(id=5, name=Billie, age=24, email=test5@baomidou.com)
      
      1
      2
      3
      4
      5

    # 配置日志输出

    使SQL可见,方便调试

    mybatis-plus:
      configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    
    1
    2
    3

    # CRUD扩展

    插入:

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

    输出为:

    image-20211101133108310

    出现现象:id被自动填充,且回写到了user对象里

    探究:主键生成策略:

    1. 自增id

    2. UUID

    3. zookeeper生成

    4. redis生成

    5. 雪花算法(snowflake)

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

    其中,IdType枚举类中包含以下几个常量

    public enum IdType {
        AUTO(0),//id自增
        NONE(1),//不设置
        INPUT(2),//手动输入
        ASSIGN_ID(3),//默认的全局唯一id策略
        ASSIGN_UUID(4);//UUID
        private final int key;
        private IdType(int key) {
            this.key = key;
        }
        public int getKey() {
            return this.key;
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14

    一般使用ASSIGN_ID作为唯一主键生成策略

    mybatis-plus主键自增生成策略:在实体类中加注解,IdType为AUTO,同时数据库中字段也要为自增

    public class User {
        @TableId(type = IdType.AUTO)
        private Long id;
        private String name;
        private Integer age;
        private String email;
    }
    
    1
    2
    3
    4
    5
    6
    7

    测试:此前数据库中只有id为1-5

    image-20211101133113508

    更新

    @Test
        public void testUpdate(){
            User user = new User();
            user.setId(6L).setName("zdk222").setAge(20).setEmail("369365576@qq.com");
            int result = userMapper.updateById(user);
            System.out.println("result = " + result);
            System.out.println("user = " + user);
        }
    
    1
    2
    3
    4
    5
    6
    7
    8

    image-20211101133117095

    mybatis-plus会根据条件自动拼接SQL

    自动填充一些字段

    创建时间、更新时间等

    方式一:数据库级别

    1. 在表中新增字段create_time,update_time(类型为datetime时)
    2. 将数据库字段设置为根据当前时间戳更新

    方式二:代码层面填充

    1. 使用tableField注解,fill是填充,值为:当执行insert、update等操作时填充属性
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
    
    1
    2
    3
    4
    5
    1. 编写处理器处理注解

      • 自定义自己的metaObjectHandler类,实现MetaObjectHandler接口

        @Slf4j
        @Component
        public class MyMetaObjectHandler implements MetaObjectHandler {
            @Override
            public void insertFill(MetaObject metaObject) {
                log.info("插入时开始填充--------");
                setFieldValByName("createTime",new Date(),metaObject);
                setFieldValByName("updateTime",new Date(),metaObject);
            }
        
            @Override
            public void updateFill(MetaObject metaObject) {
                log.info("更新时开始填充--------");
                setFieldValByName("updateTime",new Date(),metaObject);
            }
        }
        
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
      • 使用setFieldValByName方法即可按属性名、要填充的值来填充数据

      • 执行测试即可

    # 乐观锁、悲观锁:

    乐观锁:它总是认为不会出现问题,无论干什么不去上锁!如果出现了问题,再次更新值测试

    悲观锁:它总是认为总是出现问题,无论干什么都会上锁!

    乐观锁:当要更新一条记录时,希望这条记录没有被别人更新

    乐观锁实现方式:

    • 取出记录时,获取当前version
    • 更新时,带上这个version,
    • 执行更新时,set version =newVersion where version =oldVersion
    • 如果version不对,就会更新失败
    乐观锁:1、先查询,获得版本号 version = 1
    -- A
    update user set name = "kuangshen", version = version + 1
    where id = 2 and version = 1
    
    --B线程抢先完成,这个时候version = 2,会导致A修改失败!
    update user set name = "kuangshen", version = version + 1
    where id = 2 and version = 1
    
    -- 最终,A线程更新时version已变为2,A更新就会失败
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    Java实现乐观锁

    • 数据库表增加version字段(默认值为1),实体类增加version属性

    • 在实体类属性上加注解@Version,表示个属性是个乐观锁

      @Version
      private Integer version;
      
      1
      2
    • 注册组件:

      public class MybatisPlusConfig {
          @Bean
          public MybatisPlusInterceptor mybatisPlusInterceptor() {
              //新版乐观锁插件
              mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
              return mybatisPlusInterceptor;
          }
      
      1
      2
      3
      4
      5
      6
      7

      进行测试:

      @Test
          public void testLock(){
              User user1 = userMapper.selectById(8L);
              user1.setName("乐观锁测试用户1111").setAge(100).setEmail("xxx");
      
              User user2 = userMapper.selectById(8L);
              user2.setName("乐观锁测试用户2222").setAge(202).setEmail("aaaa");
              userMapper.updateById(user2);
              //线程user1被插队,version变为了3,但user1执行时还是按version为2区更新,所以更新失败
              userMapper.updateById(user1);
          }
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11

      测试结果:

      image-20211101133124836

    # 基本查询操作

    @Test
        public void testSelectMethod(){
            //1 查询list
            List<User> users = userMapper.selectList(null);
            //2 按id查询
            User user = userMapper.selectById(8L);
            //3 按id批量查询
            List<User> userList = userMapper.selectBatchIds(Arrays.asList(1, 2, 3, 4));
            //4 通过map进行条件查询:查询name=张迪凯的数据
            HashMap<String, Object> params = new HashMap<>();
            params.put("name", "张迪凯");
            List<User> list = userMapper.selectByMap(params);
           
        }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14

    mybatis-plus分页查询插件:

    如何使用

    1. 配置拦截器组件

      public class MybatisPlusConfig {
          /**
           * 新版
           */
          @Bean
          public MybatisPlusInterceptor mybatisPlusInterceptor() {
              MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
              //新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false
              // 避免缓存出现问题(该属性会在旧插件移除后一同移除)
              mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
              //乐观锁插件
              mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
              return mybatisPlusInterceptor;
          }
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
    2. new出Page泛型对象并设置页数和每页size,然后进行查询,结果回显到Page对象

      @Test
          public  void testPage(){
              //参数一:当前页;参数二:页面大小
              Page<User> pages = new Page<>(1,3);
              //执行分页查询
              userMapper.selectPage(pages, null);
              //获取分页查询结果
              pages.getRecords().forEach(System.out::println);
              System.out.println("pages.getTotal() = " + pages.getTotal());
          }
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10

    # 删除操作

    @Test
        public  void testDelete(){
            //1.通过id删除
            int delete = userMapper.deleteById(1L);
            System.out.println("delete = " + delete);
            //2.批量删除
            int batchIds = userMapper.deleteBatchIds(Arrays.asList(1, 2, 3));
            System.out.println("batchIds = " + batchIds);
            //3 通过map进行条件删除:删除name=张迪凯的数据
            HashMap<String, Object> params = new HashMap<>();
            params.put("name", "张迪凯");
            int deleteByMap = userMapper.deleteByMap(params);
            System.out.println("deleteByMap = " + deleteByMap);
        }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14

    # 逻辑删除

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

    逻辑删除:没有从数据库移除。是使用一个变量来标记这条数据已"被删除",只是不查询出来deleted=0->1

    管理员可以查看被删除的记录。防止数据丢失,类似于回收站

    • 需要在数据表中新加deleted字段,类型为tinyint,默认值为0,当"被删除"时,set为1

    • 步骤1.配置com.baomidou.mybatisplus.core.config.GlobalConfig$DbConfig

      mybatis-plus:
        global-config:
          db-config:
            logic-delete-field: flag  # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
            logic-delete-value: 1 # 逻辑已删除值(默认为 1)
            logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
      
      1
      2
      3
      4
      5
      6
    • 步骤2.实体类添加deleted字段,并加上注解@TableLogic(since 3.3.0,配置后可以忽略不配置)

      @TableLogic
      private Boolean deleted;
      
      1
      2
    • 测试

      @Test
      public void testLogicDelete(){
         userMapper.deleteById(1L);
      }
      
      1
      2
      3
      4

      实际走的是更新操作:

      ==>  Preparing: UPDATE user SET deleted=1 WHERE id=? AND deleted=0
      ==> Parameters: 1(Long)
      <==    Updates: 1
      
      1
      2
      3

      这样删除以后再通过id去查这条数据,就查询不到了,但数据库中数据还在。

    # 条件构造器

    使用queryWrapper或updateWrapper构造查询条件,类似以下

    @Test
        public void testWrapper(){
            QueryWrapper<User> wrapper = new QueryWrapper<>();
            wrapper.ge("id", 4);
            List<User> users = userMapper.selectList(wrapper);
            users.forEach(System.out::println);
        }
    
    1
    2
    3
    4
    5
    6
    7

    使用LambdaQueryWrapper

    • 构造LambdaQueryWrapper对象,传入参数执行查询

      @Test
          public void testLambdaQueryWrapper(){
              LambdaQueryWrapper<User> lambdaQueryWrapper = new QueryWrapper<User>().lambda();
              lambdaQueryWrapper.eq(User::getId,5);
              User user = userMapper.selectOne(lambdaQueryWrapper);
              System.out.println("user = " + user);
      }
      
      1
      2
      3
      4
      5
      6
      7

    # 代码生成器

    public class MybatisPlusCodeGenerator {
        public static void main(String[] args) {
            AutoGenerator generator = new AutoGenerator();
            //配置生成策略
    
            //1.全局配置
            GlobalConfig config = new GlobalConfig();
            //获取当前目录
            String projectPath = System.getProperty("user.dir");
            config.setOutputDir(projectPath+"/src/main/java");
            config.setAuthor("zdk");
            //设置生成后是否打开对应文件夹
            config.setOpen(false);
            //设置生成是否覆盖原来的文件
            config.setFileOverride(false);
            //去掉生成的service的I前缀
            config.setServiceName("%sService");
            config.setServiceImplName("%sServiceImpl");
            config.setIdType(IdType.AUTO);
            config.setDateType(DateType.ONLY_DATE);
            config.setSwagger2(false);
            generator.setGlobalConfig(config);
    
            //2.配置数据源
            DataSourceConfig dataSourceConfig = new DataSourceConfig();
            dataSourceConfig.setDbType(DbType.MYSQL);
            dataSourceConfig.setUrl("jdbc:mysql://localhost:3306/study?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=utf8");
            dataSourceConfig.setDriverName("com.mysql.cj.jdbc.Driver");
            dataSourceConfig.setUsername("root");
            dataSourceConfig.setPassword("root");
            generator.setDataSource(dataSourceConfig);
    
            //3.包的配置
            PackageConfig packageConfig = new PackageConfig();
            packageConfig.setModuleName("genTest");
            packageConfig.setParent("com.zdk");
            packageConfig.setEntity("entity");
            packageConfig.setMapper("mapper");
            packageConfig.setService("service");
            packageConfig.setServiceImpl("service.serviceImpl");
            packageConfig.setController("controller");
            generator.setPackageInfo(packageConfig);
    
            //4.策略配置
            StrategyConfig strategyConfig = new StrategyConfig();
            //设置要(读取)映射的表
            strategyConfig.setInclude("user");
    
            strategyConfig.setNaming(NamingStrategy.underline_to_camel);
            strategyConfig.setColumnNaming(NamingStrategy.underline_to_camel);
            //自动lombok
            strategyConfig.setEntityLombokModel(true);
            //设置逻辑删除
            strategyConfig.setLogicDeleteFieldName("deleted");
            //设置自动填充
            TableFill tableInsert = new TableFill("create_time", FieldFill.INSERT);
            TableFill tableUpdate = new TableFill("update_time", FieldFill.INSERT_UPDATE);
            ArrayList<TableFill> tableFills = new ArrayList<>();
            tableFills.add(tableInsert);
            tableFills.add(tableUpdate);
            strategyConfig.setTableFillList(tableFills);
            //乐观锁
            strategyConfig.setVersionFieldName("version");
            //设置controller的驼峰命名格式
            strategyConfig.setRestControllerStyle(true);
            //请求会变成localhost:8080/hello_id_2
            strategyConfig.setControllerMappingHyphenStyle(true);
            generator.setStrategy(strategyConfig);
    
            //执行生成
            generator.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

    ​

    在 GitHub 上编辑此页 (opens new window)
    #mybatis#mybatis-plus
    最后更新: 2022/10/04, 16:10:00
    mybatis
    Spring

    ← mybatis Spring→

    Theme by Vdoing | Copyright © 2022-2025 zdk | notes
    湘ICP备2022001117号-1
    川公网安备 51142102511562号
    本网站由 提供CDN加速/云存储服务
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式