Mybatis-plus学习笔记

Mybatis-plus学习

快速入门

  1. 导入依赖

    <!--mybatis-plus-->
        <dependency>
        	<groupId>com.baomidou</groupId>
        	<artifactId>mybatis-plus-boot-starter</artifactId>
        	<version>3.0.5</version>
        </dependency>
        
    <!--数据库驱动-->
        <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        </dependency>    
    
  2. 编写实体类

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User {
        
        private Long id;
        private String name;
        private Integer age;
        private String email;
    }
    
    
  3. 编写mapper接口,接口继承BaseMapper

    @Repository
    public interface UserMapper extends BaseMapper<User> {
    }
    
  4. 开启扫描接口

    //扫描我们的mapper文件夹
    @MapperScan("com.fk.mapper")
    @SpringBootApplication
    public class MybatisPlusApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(MybatisPlusApplication.class, args);
        }
    
    }
    
  5. 测试

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

配置日志

在application.properties中配置日志

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

CRUD扩展

插入操作

  1. 实体类修改

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User {
    
        @TableId(type = IdType.AUTO)
        private Long id;
        private String name;
        private Integer age;
        private String email;
    }
    
  2. 测试

    @Test
    void testInsert(){
        User user = new User();
        user.setName("测试插入");
        user.setAge(18);
        user.setEmail("1762845033@qq.com");
    
        int result = userMapper.insert(user);//自动生成id,并且id会自动回填到user对象
        System.out.println(result);
        System.out.println(user);
    
    }
    

主键生成策略

IdType.AUTO 自增Id
IdType.NONE 未设置主键
IdType.INPUT 手动输入
IdType.ID_WORKER 默认的全局唯一id
IdType.UUID 全局唯一id uuid
IdType.ID_WORKER_STR ID_WORKER字符串表示

如果设置为INPUT手动输入,并且数据库没有设置自增,则无法插入成功

更新操作

//测试更新
    @Test
    void testUpdate(){
        //所有sql都是自动拼接的

        User user = new User();
        user.setId(1359425363165110277L);
        user.setName("测试更新");
        user.setAge(19);

        int i = userMapper.updateById(user);
        System.out.println(i);

    }

自动填充

  1. 数据库层面

    根据阿里巴巴手册,我们每个表都需要 插入时间、更新时间 这两个字段,用于数据跟踪

    所以我们在数据库中添加这两个字段 create_time update_time

    然后设置默认值为CURRENT_TIMESTAMP

    并且update_time字段勾选根据当前时间戳更新

  2. 代码层面

    • 修改实体类,为要自动填充的字段加上 @TableField 注解

      //字段添加填充内容
      //fill指定填充时机 (DEFAULT,INSERT,UPDATE,INSERT_UPDATE)
      @TableField(fill = FieldFill.INSERT)
      private Date createTime;
      
      @TableField(fill = FieldFill.INSERT_UPDATE)
      private Date updateTime;
      
    • 编写自动填充的处理器

      @Slf4j
      @Component //一定不要忘记把处理器加到IOC容器中
      public class MyMetaObjectHandler implements MetaObjectHandler {
      
          //插入时的填充策略
          @Override
          public void insertFill(MetaObject metaObject) {
              log.info("start insert fill...");
              //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);
          }
      }
      

乐观锁

当要更新一条记录的时候,希望这条记录没有被别人更新
乐观锁实现方式:

  • 取出记录时,获取当前version
  • 更新时,带上这个version
  • 执行更新时, set version = newVersion where version = oldVersion
  • 如果version不对,就更新失败
乐观锁:先查询,获取版本号 version = 1

-- A线程
update user set name = "fk", version = version + 1
where id = 2 and version = 1

-- B线程 抢先完成,这个时候 version = 2, 会导致A修改失败!
update user set name = "fk", version = version + 1
where id = 2 and version = 1

测试MP的插件

  1. 给数据库中增加version字段

    Mybatis-plus学习笔记

    说明:

    • 支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime

    • 整数类型下 newVersion = oldVersion + 1

    • newVersion 会回写到 entity

    • 仅支持 updateById(id)update(entity, wrapper) 方法

    • update(entity, wrapper) 方法下, wrapper 不能复用!!!

  2. 实体类添加version字段

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

    //扫描我们的mapper文件夹
    @MapperScan("com.fk.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("fk");
        user.setEmail("1762845033@qq.com");
        // 3.执行更新操作
        userMapper.updateById(user);
    }
    
    //测试乐观锁失败  多线程下
    @Test
    public void testOptimisticLocker2(){
        //线程1
        User user = userMapper.selectById(1L);
        user.setName("fk111");
        user.setEmail("1762845033@qq.com");
    
        //线程2
        User user2 = userMapper.selectById(1L);
        user2.setName("fk222");
        user2.setEmail("1762845033@qq.com");
        userMapper.updateById(user2);
    
        // 线程1可以使用 自旋锁来多次尝试提交!
        userMapper.updateById(user);//  如果没有乐观锁就会覆盖插队线程的值
    }
    

查询操作

// 测试查询
@Test
public void testSelectById(){
    User user = userMapper.selectById(1L);
    System.out.println(user);
}

// 测试批量查询
@Test
public void testSelectBatchIds(){
    List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
    users.forEach(System.out::println);
}

// 条件查询之一 map
@Test
public void testSelectBatchIds2(){
    HashMap<String, Object> map = new HashMap<>();
    //自定义要查询
    map.put("name","fk222");
    map.put("age", 18);


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

分页查询

  1. 原始limit进行分页
  2. pageHelper第三方插件
  3. MP自带的分页插件

如何使用

1)配置拦截器组件即可

	// 分页插件
    @Bean
    public PaginationInterceptor paginationInterceptor(){
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        // 设置请求的页面大于最大页后操作,true调回到首页,false 继续请求 默认false
        // paginationInterceptor.setOverflow(false);
        // 设置最大单页限制数量, 默认 500 条,-1 不受限制
        // paginationInterceptor.setLimit(500);
        // 开启 count 的 join 优化,只针对部分 left join
        // paginationInterceptor.setCountSqlParser(new 	JsqlParserCountOptimize(true));
        return paginationInterceptor;
    }

2)直接使用

	// 测试分页查询
    @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(){
        userMapper.deleteById(1359425363165110277L);
    }

    // 通过id批量删除
    @Test
    public void testdeleteBatchIds(){
        userMapper.deleteBatchIds(Arrays.asList(1359425363165110276L,5L,4L));
    }

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

逻辑删除

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

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

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

测试一下!

1)在数据库中增加一个deleted字段

Mybatis-plus学习笔记

2)在实体类中增加属性

	@TableLogic //逻辑删除
    private Integer deleted;

3)配置

	//逻辑删除组件
    @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)测试删除

Mybatis-plus学习笔记

数据库中的记录依然存在,但是deleted的值发生了变化

Mybatis-plus学习笔记

Mybatis-plus学习笔记

性能分析插件

作用:性能分析拦截器,用于输出每条SQL语句及其执行时间

MP提供了性能分析插件,如果超过这个时间就停止运行!

1)导入插件

	/**
         该插件很多功能已经不可用了,只能打印执行时间和执行sql语句
     */
    @Bean
    @Profile({"dev","test"}) // 设置 dev test 环境开启,保证开发效率
    public PerformanceInterceptor performanceInterceptor(){
        PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
        performanceInterceptor.setMaxTime(1); //ms毫秒为单位 设置sql执行的最大时间,如果超过了则不执行
        performanceInterceptor.setFormat(true); //开启格式化支持
        return new PerformanceInterceptor();
    }

注意:要在SpringBoot中配置环境为dev或者test

条件构造器

测试一

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

测试二

	@Test
    void test2(){
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("name","fk");
        User user = userMapper.selectOne(wrapper); // 查询一个数据,出现多个结果用 List 或者 Map
        System.out.println(user);
    }

测试三

	@Test
    void test3(){
        // 查询年龄在 20-30 岁之间用户
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.between("age",10,18); // 区间

        Integer count = userMapper.selectCount(wrapper);// 查询结果数
        System.out.println(count);
    }

测试四,记住查看输出的SQL进行分析

// 模糊查询
    @Test
    void test4(){
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        // like -> (左和右) -> %o%    likeLeft表示 % 在左边
        wrapper.notLike("name","fk")
                .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.in("id","select id from user where id < 3");

        List<Object> objects = userMapper.selectObjs(wrapper);// 返回值为List
        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);
    }

代码自动生成器

// 代码生成器
public class MyCode {

    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("fengkai");
        gc.setOpen(false); //是否打开资源管理器
        gc.setFileOverride(false); // 是否覆盖原有代码
        gc.setServiceName("%sService"); // 去Service的I前缀
        //这里也是优先读取数据库中的id策略,如果数据库没有设置,则使用我们自己设置的id策略
        gc.setIdType(IdType.ID_WORKER);
        gc.setDateType(DateType.ONLY_DATE);
        gc.setSwagger2(true);

        mpg.setGlobalConfig(gc);

        // 2.设置数据源
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUsername("root");
        dsc.setPassword("root");
        dsc.setUrl("jdbc:mysql://localhost:3306/mybatisplus?useUnicode=true&useSSL=false&serverTimezone=UTC&characterEncoding=utf-8");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setDbType(DbType.MYSQL);

        mpg.setDataSource(dsc);

        // 3.包的配置
        PackageConfig pc = new PackageConfig();
        pc.setModuleName("demo"); //模块的包名
        pc.setParent("com.fk");
        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);
        strategy.setLogicDeleteFieldName("deleted");//逻辑删除字段
        // 自动填充策略
        TableFill createTime = new TableFill("create_time", FieldFill.INSERT);
        TableFill updateTime = new TableFill("update_time", FieldFill.INSERT_UPDATE);
        ArrayList<TableFill> tableFills = new ArrayList<>();
        tableFills.add(createTime);
        tableFills.add(updateTime);
        strategy.setTableFillList(tableFills);
        // 乐观锁
        strategy.setVersionFieldName("version");

        strategy.setRestControllerStyle(true);
        strategy.setControllerMappingHyphenStyle(true); // localhost:8080/hello_id_2

        mpg.setStrategy(strategy);

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

注意事项

  • AutoGenerator是 com.baomidou.mybatisplus.generator 包下的,不要导错了

  • 该有的config配置类还是要配置

  • application.properties中的相应配置还是要写,并没有自动生成

上一篇:Android实现多线程断点续传


下一篇:MybatisPlus的学习总结