MyBatisPlus Study Notes

1 MyBatisPlus概述

1.1 MyBatis介绍

  • MyBatisPlus(简称MP)基于MyBatis框架基础上开发的增强型工具,旨在简化开发、提高效率
  • MyBatisPlus官网

1.2 MyBatisPlus特性

  • 无侵入:只做增强不做改变,不会对现有工程产生影响
  • 强大的 CRUD 操作:内置通用 Mapper,少量配置即可实现单表CRUD 操作
  • 支持 Lambda:编写查询条件无需担心字段写错
  • 支持主键自动生成
  • 内置分页插件
  • ……

2 标准数据层开发

2.1 MyBatisPlus的CRUD操作API

在这里插入图片描述

2.2 分页功能接口实现

在这里插入图片描述

2.2.1 config(配置层)拦截器实现

此处拦截SQL语句,目的是为了拼接,分页条件

@Configuration
public class MpConfig {
    @Bean
    public MybatisPlusInterceptor mpInterceptor(){
        //1.定义Mp拦截器 ,创建MybatisPlusInterceptor拦截器对象
        MybatisPlusInterceptor mpInterceptor = new MybatisPlusInterceptor();
        //2.添加具体的拦截器、添加分页拦截器
        mpInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return mpInterceptor;
    }
}

2.2.2 Dao(Mapper)数据访问层(CRUD)操作

实现BaseMapper<>接口

@Mapper
public interface UserDao extends BaseMapper<User> {
}

2.2.3 Junit单元测试进行测试

    @Test
    public void testPage() {
        IPage<User> page = new Page(2, 5);// 分页构造器:设置 当前第几页 一页多少条
        IPage<User> pageResult = userDao.selectPage(page, null);
        System.out.println(JSON.toJSONString(page));
        System.out.println("数据列表" + JSON.toJSONString(pageResult.getRecords()));
        System.out.println("当前页码" + pageResult.getCurrent());
        System.out.println("每页条数" + pageResult.getSize());
        System.out.println("总记录数" + pageResult.getTotal());
        System.out.println("总页数" + pageResult.getPages());
    }
}

2.3 开启MyBatisPlus日志

# 开启mp的日志(输出到控制台)
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

2.4 取消初始化spring日志打印

做法:在resources下新建ogback.xml文件内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>

</configuration>

2.5 取消SpringBoot启动banner图标、关闭mybatisplus启动图标

spring:
  main:
    banner-mode: off # 关闭SpringBoot启动图标(banner)
# mybatis-plus日志控制台输出
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config:
    banner: off # 关闭mybatisplus启动图标

3 MyBatisPlus DQL

3.1 条件查询API

在这里插入图片描述

3.2 条件查询

3.2.1 方式一:按条件查询(常规格式)

//方式一:按条件查询
    @Test
    public void test1(){
        // select * from user where age >= 18 and age < 65
        QueryWrapper<User> qw = new QueryWrapper<>();
        qw.ge("age",18);
        qw.lt("age",65);
        List<User> userList = userDao.selectList(qw);
        System.out.println(JSON.toJSONString(userList)); //[{"age":28,"id":5,"name":"snake","password":"123456","tel":"12345678910"},{"age":22,"id":6,"name":"张益达","password":"123456","tel":"12345678910"}]

        /*
         System.out.println(userList);
        不进行JSON转换输出[User(id=5, name=snake, password=123456, age=28, tel=12345678910), User(id=6, name=张益达, password=123456, age=22, tel=12345678910)]
        * */
    }

3.2.2 方式二:按条件查询(lambda格式)

查阅源码优化全局变量处声明对象操作
在这里插入图片描述在这里插入图片描述

3.2.2.1全局变量声明

    LambdaQueryWrapper<User> userLambdaQueryWrapper = Wrappers.lambdaQuery();//lambdaQuery 可用屏蔽底层的具体实现,未来会有变化上层代码无需过多的调整。而且不用new对象减少内存
    @Autowired
    private UserDao userDao;

3.2.2.2 select * from user where age >= 18 and age < 65

传统语法MyBatisPlus语法说明
<ltless than
<=leless equal
>gtgreater than
>=gegreater equal
=eqequal
between and范围
like模糊查询
in在in之后的列表中的值,多选一
	//lambda格式
    @Test
    public void test2(){
        // select * from user where age >= 18 and age < 65

        //LambdaQueryWrapper<User> userLambdaQueryWrapper1 = new LambdaQueryWrapper<>();
       // LambdaQueryWrapper<User> userLambdaQueryWrapper = Wrappers.lambdaQuery();
        userLambdaQueryWrapper.ge(User::getAge,18).lt(User::getAge,65);
        List<User> userList = userDao.selectList(userLambdaQueryWrapper);
        System.out.println(JSON.toJSONString(userList));
        System.out.println("=================");
        System.out.println(userList);
    }
 // 等于
    @Test
    public void test3(){
        //select * from user where name = "tom"
        userLambdaQueryWrapper.eq(User::getName,"tom");
        List<User> userList = userDao.selectList(userLambdaQueryWrapper);
        System.out.println(JSON.toJSONString(userList));
    }

3.3 MyBatisPlus中null值判断

 /**
     * null值判断
     * 判断 字段值是否为null 不为null才拼接查询条件
     */
    @Test
    public void test31(){
        //模拟前端传的参数
        //select * from user where name = null
        User user = new User();
      /*  if (user.getName()!=null){
            userLambdaQueryWrapper.eq(User::getName,user.getName());
        }健壮性判断*/

        userLambdaQueryWrapper.eq(user.getName()!= null,User::getName,user.getName());
        //此处user.getName()为空,User::getName与user.getName()作对比,
        //      即  在数据库实体类中的name属性="null" 做判断条件



        List<User> userList = userDao.selectList(userLambdaQueryWrapper);
        System.out.println(JSON.toJSONString(userList));
    }

 // like模糊查询
    @Test
    public void test4(){
        //select * from user where name like "j%"

        userLambdaQueryWrapper.like(User::getName,"j");
        List<User> list = userDao.selectList(userLambdaQueryWrapper);
        System.out.println(list);
    }
 /**
     * between
     */
    @Test
    public void test5(){
        //select * from user where age between 16 and 28

        userLambdaQueryWrapper.between(User::getAge, 16, 28);
        List<User> list = userDao.selectList(userLambdaQueryWrapper);
        System.out.println(list);
    }
    /**
     * in
     */
    @Test
    public void test6(){
        //select * from user where id in (1,2,3)
        List<Integer> inList = Arrays.asList(1, 2, 3);
        userLambdaQueryWrapper.in(User::getId, inList);
        List<User> list = userDao.selectList(userLambdaQueryWrapper);
        System.out.println(list);
    }

3.4 批量(Batch)操作

    /**
     * 根据id列表批量查询
     */
    @Test
    public void test1(){
        List<Integer> ids = Arrays.asList(1, 2, 3);//将一个变长参数或者数组转换成List
        List<User> userList = userDao.selectBatchIds(ids);
        System.out.println(userList);
    }
    /**
     * 根据id列表批量删除
     */
    @Test
    public void test2(){
        List<Integer> ids = Arrays.asList(1, 2, 3);
        int count = userDao.deleteBatchIds(ids);
        System.out.println(count);
    }

3.5 查询投影(聚合查询)【查询字段、分组、分页】

    /**
     * 聚合查询一般用: selectMaps
     */
    @Test
    public void test(){
        //select tel, count(*) as cnt from user group by tel order by cnt;

        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
        userQueryWrapper.select("tel","count(*) as cnt");
        userQueryWrapper.groupBy("tel");
        userQueryWrapper.orderByAsc("cnt");
        List<Map<String, Object>> mapList = userDao.selectMaps(userQueryWrapper); //selectMaps:根据 Wrapper 条件,查询全部记录
        System.out.println(JSON.toJSONString(mapList));
    }

3.6 字段映射与表名映射

注解说明
@TableField通过value属性(value="数据库中实际字段名"),设置当前属性对应的数据库表中的字段关系
@TableField通过exist属性(true存在;false不存在),设置属性在数据库表字段中是否存在,默认为true。不能与value合并使用
@TableField通过select属性(true参与;false不参与):设置该属性是否参与查询。此属性与select()映射配置不冲突。
@TableName通过value属性@TableName("数据库中实际表名"),设置当前类对应的数据库表名称

4 DML编程控制

4.1 id生成策略控制(Insert)

注解说明
@TableId 1、AUTO(0): 使用数据库id自增策略控制id生成;2、NONE(1):不设置id生成策略;3、INPUT(2):用户手工输入id;4、ASSIGN_ID(3):雪花算法生成id (可兼容数值型与字符串型);5、ASSIGN UUID(4):以UUID生成算法作为id生成策略

4.1.1 全局配置

mybatis-plus:
  global-config:
    db-config:
      id-type: assign_id
      table-prefix: tbl_

4.2 逻辑删除(Delete/Update)

4.2.1 数据库表中添加逻辑删除字段

在这里插入图片描述

4.2.2 实体类中添加对应字段,并设定当前字段为逻辑删除标记字段

package com.itheima.domain;

import com.baomidou.mybatisplus.annotation.*;

import lombok.Data;

@Data
public class User {

    private Long id;
    
    //逻辑删除字段,标记当前记录是否被删除
    @TableLogic
    private Integer deleted;
    
}

4.2.2.1 全局配置逻辑删除字面值(不建议)

mybatis-plus:
  global-config:
    db-config:
      table-prefix: tbl_
      # 逻辑删除字段名
      logic-delete-field: deleted
      # 逻辑删除字面值:未删除为0
      logic-not-delete-value: 0
      # 逻辑删除字面值:删除为1
      logic-delete-value: 1

5 乐观锁(Update)

在这里插入图片描述

乐观锁的解决思想(版本号机制或 CAS 算法):
版本号机制:首先,给数据库添加一个version(int)的标记字段,然后,在多个线程同时访问数据库时也都获取到version的值,在提交更新时,若刚才读取到的version为当前数据库中的version时才更新,更新过后version值会发生改变,其他线程再提交更新时,获取到的version值和当前数据库version值不一致,提交更新失败。

举一个简单的例子 :假设数据库中帐户信息表中有一个 version 字段,当前值为 1 ;而当前帐户余额字段( balance )为 $100 。

  1. 操作员 A 此时将其读出( version=1 ),并从其帐户余额中扣除 $50( $100-$50 )。
  2. 在操作员 A 操作的过程中,操作员 B 也读入此用户信息( version=1 ),并从其帐户余额中扣除 $20 ( $100-$20 )。
  3. 操作员 A 完成了修改工作,将数据版本号( version=1 ),连同帐户扣除后余额( balance=$50 ),提交至数据库更新,此时由于提交数据版本等于数据库记录当前版本,数据被更新,数据库记录 version 更新为 2 。
  4. 操作员 B 完成了操作,也将版本号( version=1 )试图向数据库提交数据( balance=$80 ),但此时比对数据库记录版本时发现,操作员 B 提交的数据版本号为 1 ,数据库记录当前版本也为 2 ,不满足 “ 提交版本必须等于当前版本才能执行更新 “ 的乐观锁策略,因此,操作员 B 的提交被驳回。

这样就避免了操作员 B 用基于 version=1 的旧数据修改的结果覆盖操作员 A 的操作结果的可能。

5.2 乐观锁案例解决

5.2.1 数据库中添加标记字段version

在这里插入图片描述

5.2.2 实体类中添加相应字段,并设定当前字段为逻辑删除标记字段

注解说明
@VersionMP中自带对乐观锁定支持的注解
@Data
public class User {
	private Long id;
    @Version
    private Integer version;
}

5.2.3 配置乐观锁拦截器实现锁机制对应的动态SQL语句拼接

@Configuration
public class MpConfig {
    @Bean
    public MybatisPlusInterceptor mpInterceptor() {
        //1.定义Mp拦截器
        MybatisPlusInterceptor mpInterceptor = new MybatisPlusInterceptor();

        //2.添加乐观锁拦截器
        mpInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        
        return mpInterceptor;
    }
}

5.2.4 使用乐观锁机制在修改前必须先获取到对应数据的verion方可正常进行

@Test
public void testUpdate() {
    /*User user = new User();
    user.setId(3L);
    user.setName("Jock666");
    user.setVersion(1);
    userDao.updateById(user);*/
    
    //1.先通过要修改的数据id将当前数据查询出来
    //User user = userDao.selectById(3L);
    //2.将要修改的属性逐一设置进去
    //user.setName("Jock888");
    //userDao.updateById(user);
    
    //1.先通过要修改的数据id将当前数据查询出来
    User user = userDao.selectById(3L);     //version=3
    User user2 = userDao.selectById(3L);    //version=3
    user2.setName("Jock aaa");
    userDao.updateById(user2);              //version=>4
    user.setName("Jock bbb");
    userDao.updateById(user);               //verion=3?条件还成立吗?
}

6 简单CURD代码快速生成器

按以下方法操作,注意:此插件只针对单表简单CURD操作有效

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值