Mybatis和Mybatisplus

1.1 Mybatis概述

1.1.1【Mybatis概念】

问题1:Mybatis是什么,能干嘛?

MyBatis 是一款优秀的 持久层框架,用于简化 JDBC 开发(对jdbc代码进行了封装)

框架:框架就是一个半成品软件,是一套可重用的、通用的、软件基础代码模型

持久层:JavaEE三层架构:表现层、业务层、持久层

问题2:JDBC存在的缺点?

  • 硬编码

    • 注册驱动、获取连接

    • SQL语句

  • 操作繁琐

    • 手动设置参数

    • 手动封装结果集

问题3:Mybatis是如何进行规避的?

  • 硬编码可以配置到配置文件

  • 操作繁琐的地方mybatis都自动完成

Mybatis和Mybatisplus_第1张图片

2.1 MyBatisPlus简介

2.1.1【MyBatisPlus简介】

问题1:MyBatisPlus具体有哪些特性?

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

  • 官网:https://mybatis.plus/ https://mp.baomidou.com/

MyBatisPlus特性

  • 无侵入:只做增强不做改变,不会对现有工程产生影响

  • 强大的 CRUD 操作:内置通用 Mapper,少量配置即可实现单表CRUD 操作

  • 支持 Lambda:编写查询条件无需担心字段写错

  • 支持主键自动生成

  • 内置分页插件

  • ……

2.2 标准CRUD制作

2.2 【标准CRUD制作】

问题1:MyBatisPlus内置通用Mapper提供了哪些API?

Mybatis和Mybatisplus_第2张图片

 

问题2:Lombok插件的功能?

可以自动生成实体类的GET、SET方法

Lombok,一个Java类库,提供了一组注解,简化POJO实体类开发。


    org.projectlombok
    lombok
    1.18.12

常用注解:

@Data,为当前实体类在编译期设置对应的get/set方法,无参/无参构造方法,toString方法,hashCode方法,equals方法等

代码展示:

import lombok.*;
/*
    1 生成getter和setter方法:@Getter、@Setter
      生成toString方法:@ToString
      生成equals和hashcode方法:@EqualsAndHashCode

    2 统一成以上所有:@Data

    3 生成空参构造: @NoArgsConstructor
      生成全参构造: @AllArgsConstructor

    4 lombok还给我们提供了builder的方式创建对象,好处就是可以链式编程。 @Builder【扩展】
 */
@Data
public class User {
    private Long id;
    private String name;
    private String password;
    private Integer age;
    private String tel;

2.3 标准分页功能制作

2.3.1 【标准分页功能制作】

问题1:MyBatisPlus实现分页功能步骤?

分页功能接口

Mybatis和Mybatisplus_第3张图片

 MyBatisPlus分页使用

①:设置分页拦截器作为Spring管理的bean

import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MybatisPlusConfig {
    
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        //1 创建MybatisPlusInterceptor拦截器对象
        MybatisPlusInterceptor mpInterceptor=new MybatisPlusInterceptor();
        //2 添加分页拦截器
        mpInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return mpInterceptor;
    }
}

②:执行分页查询

//分页查询
@Test
void testSelectPage(){
    //1 创建IPage分页对象,设置分页参数
    IPage page=new Page<>(1,3);
    //2 执行分页查询
    userDao.selectPage(page,null);
    //3 获取分页结果
    System.out.println("当前页码值:"+page.getCurrent());
    System.out.println("每页显示数:"+page.getSize());
    System.out.println("总页数:"+page.getPages());
    System.out.println("总条数:"+page.getTotal());
    System.out.println("当前页数据:"+page.getRecords());
}

问题2:Mybatis分页底层实现原理?

拦截器

3.1 条件查询的三种格式

3.1.1 【条件查询的三种格式】

问题1:条件查询的三种格式?

Mybatis和Mybatisplus_第4张图片

 

方式一:按条件查询

//方式一:按条件查询
QueryWrapper qw=new QueryWrapper<>();
qw.lt("age", 18);
List userList = userDao.selectList(qw);
System.out.println(userList);

方式二:lambda格式按条件查询

//方式二:lambda格式按条件查询
QueryWrapper qw = new QueryWrapper();
qw.lambda().lt(User::getAge, 10);
List userList = userDao.selectList(qw);
System.out.println(userList);

方式三:lambda格式按条件查询(推荐)

//方式三:lambda格式按条件查询
LambdaQueryWrapper lqw = new LambdaQueryWrapper();
lqw.lt(User::getAge, 10);
List userList = userDao.selectList(lqw);
System.out.println(userList);

并且关系(and)

//并且关系
LambdaQueryWrapper lqw = new LambdaQueryWrapper();
//并且关系:10到30岁之间
lqw.lt(User::getAge, 30).gt(User::getAge, 10);
List userList = userDao.selectList(lqw);
System.out.println(userList);

或者关系(or)

//或者关系
LambdaQueryWrapper lqw = new LambdaQueryWrapper();
//或者关系:小于10岁或者大于30岁
lqw.lt(User::getAge, 10).or().gt(User::getAge, 30);
List userList = userDao.selectList(lqw);
System.out.println(userList);

3.2 条件查询null判定

3.2.1 【条件查询null判定】

问题1:搜索场景,在多条件查询中,有条件的值为空应该怎么解决?

if语句控制条件追加

Integer minAge=10;  //将来有用户传递进来,此处简化成直接定义变量了
Integer maxAge=null;  //将来有用户传递进来,此处简化成直接定义变量了
LambdaQueryWrapper lqw = new LambdaQueryWrapper();
if(minAge!=null){
    lqw.gt(User::getAge, minAge);
}
if(maxAge!=null){
    lqw.lt(User::getAge, maxAge);
}
List userList = userDao.selectList(lqw);
userList.forEach(System.out::println);

问题2:MyBatisPlus是如何对NULL值进行处理的?

条件参数控制

Integer minAge=10;  //将来有用户传递进来,此处简化成直接定义变量了
Integer maxAge=null;  //将来有用户传递进来,此处简化成直接定义变量了
LambdaQueryWrapper lqw = new LambdaQueryWrapper();
//参数1:如果表达式为true,那么查询才使用该条件
lqw.gt(minAge!=null,User::getAge, minAge);
lqw.lt(maxAge!=null,User::getAge, maxAge);
List userList = userDao.selectList(lqw);
userList.forEach(System.out::println);

条件参数控制(链式编程)

Integer minAge=10;  //将来有用户传递进来,此处简化成直接定义变量了
Integer maxAge=null;  //将来有用户传递进来,此处简化成直接定义变量了
LambdaQueryWrapper lqw = new LambdaQueryWrapper();
//参数1:如果表达式为true,那么查询才使用该条件
lqw.gt(minAge!=null,User::getAge, minAge)
   .lt(maxAge!=null,User::getAge, maxAge);
List userList = userDao.selectList(lqw);
userList.forEach(System.out::println);

3.3 查询投影,设置【查询字段、分组、分页】

3.3.1 【查询投影】

问题1:如何指定需要查询字段?

查询结果包含模型类中部分属性

/*LambdaQueryWrapper lqw = new LambdaQueryWrapper();
lqw.select(User::getId, User::getName, User::getAge);*/
//或者
QueryWrapper lqw = new QueryWrapper();
lqw.select("id", "name", "age", "tel");

List userList = userDao.selectList(lqw);
System.out.println(userList);

查询结果包含模型类中未定义的属性

QueryWrapper lqw = new QueryWrapper();
lqw.select("count(*) as count, tel");
lqw.groupBy("tel");

List> userList = userDao.selectMaps(lqw);
System.out.println(userList);

问题2:如果查询投影无法满足需求,应该如何处理?

在Mapper接口中,自定义SQL和查询接口

3.4 查询条件设置

3.4.1 【查询条件设置】

问题1:多条件查询有哪些组合?

多条件查询有哪些组合?

  • 范围匹配(> 、 = 、between)

  • 模糊匹配(like)

  • 空判定(null)

  • 包含性匹配(in)

  • 分组(group)

  • 排序(order)

  • ……

查询条件案例:

用户登录(eq匹配)

LambdaQueryWrapper lqw = new LambdaQueryWrapper();
//等同于=
lqw.eq(User::getName, "Jerry").eq(User::getPassword, "jerry");
User loginUser = userDao.selectOne(lqw);
System.out.println(loginUser);

  购物设定价格区间、户籍设定年龄区间(le ge匹配 或 between匹配)

LambdaQueryWrapper lqw = new LambdaQueryWrapper();
//范围查询 lt le gt ge eq between
lqw.between(User::getAge, 10, 30);
List userList = userDao.selectList(lqw);
System.out.println(userList);

查信息,搜索新闻(非全文检索版:like匹配)

LambdaQueryWrapper lqw = new LambdaQueryWrapper();
//模糊匹配 like
lqw.likeLeft(User::getName, "J");
List userList = userDao.selectList(lqw);
System.out.println(userList);

统计报表(分组查询聚合函数)

QueryWrapper qw = new QueryWrapper();
qw.select("gender","count(*) as nums");
qw.groupBy("gender");
List> maps = userDao.selectMaps(qw);
System.out.println(maps);

3.5 映射匹配兼容性

3.5.1 【映射匹配兼容性】

问题1:如何处理数据表字段和Java实体属性不匹配问题?

表字段与编码属性设计不同步

  • 在模型类属性上方,使用@TableField属性注解,通过value属性,设置当前属性对应的数据库表中的字段关系。

Mybatis和Mybatisplus_第5张图片

 

问题2:如何处理数据表字段不存在,Java实体属性存在问题?

编码中添加了数据库中未定义的属性

  • 在模型类属性上方,使用@TableField注解,通过exist属性,设置属性在数据库表字段中是否存在,默认为true。此属性无法与value合并使用。

Mybatis和Mybatisplus_第6张图片

 

问题3:如何实现数据表中字段不参与查询,保证数据安全性?

  • 在模型类属性上方,使用@TableField注解,通过==select==属性:设置该属性是否参与查询。此属性与select()映射配置不冲突。

Mybatis和Mybatisplus_第7张图片

 

问题4:如何处理 表名和实体名称不一致问题?

  • 模型类上方,使用@TableName注解,通过==value==属性,设置当前类对应的数据库表名称。

Mybatis和Mybatisplus_第8张图片

 

@Data
@TableName("tbl_user")
public class User {
    /*
        id为Long类型,因为数据库中id为bigint类型,
        并且mybatis有自己的一套id生成方案,生成出来的id必须是Long类型
     */
    private Long id;
    private String name;
    @TableField(value = "pwd",select = false)
    private String password;
    private Integer age;
    private String tel;
    @TableField(exist = false) //表示online字段不参与CRUD操作
    private Boolean online;
}

4.1 id生成策略

4.1.1 【id生成策略】

问题1:一个ID一般来说有哪些要求?

  • 全局唯一:不能出现重复的ID号,既然是唯一标识,这是最基本的要求。

  • 趋势递增:确保生成的ID是对于某个用户或者业务是按一定的数字有序递增的。

  • 高可用性:确保任何时候都能正确的生成ID。

  • 带时间:ID里面包含时间,一眼扫过去就知道哪天的交易。

问题2:id生成策略有哪些?

  1. UUID

    Java自带的生成UUID的方式就能生成一串唯一随机32(32个16进制数字)位长度数据,而且够我们用N亿年,全球唯一。

  2. 数据库自增ID

  3. 中间件

  4. 开源算法Snowflake

    指定机器 & 同一时刻 & 某一并发序列,是唯一的,据此可生成一个64 bits的唯一ID(long)。

id生成策略控制(@TableId注解)

  • 名称:@TableId

  • 类型:属性注解

  • 位置:模型类中用于表示主键的属性定义上方

  • 作用:设置当前类中主键属性的生成策略

  • 相关属性

                type:设置主键属性的生成策略,值参照IdType枚举值

Mybatis和Mybatisplus_第9张图片

 全局策略配置

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

id生成策略全局配置

Mybatis和Mybatisplus_第10张图片

 

表名前缀全局配置

Mybatis和Mybatisplus_第11张图片

 

问题3:实际工作最常用的id生成策略?

看具体需求,推荐使用snowflake

4.2 多数据操作(删除与查询)

4.2.2 【多数据操作(删除与查询)】

问题1:mp如何实现批量删除?

//删除指定多条数据
List list = new ArrayList<>();
list.add(1402551342481838081L);
list.add(1402553134049501186L);
list.add(1402553619611430913L);

userDao.deleteBatchIds(list);

问题2:mp如何实现批量删除?

//查询指定多条数据
List list = new ArrayList<>();
list.add(1L);
list.add(3L);
list.add(4L);
userDao.selectBatchIds(list);

4.3 逻辑删除

4.3.1 【逻辑删除】

问题1:逻辑删除使用场景?

在实际环境中,如果想删除一条数据,是否会真的从数据库中删除该条数据?

  • 删除操作业务问题:业务数据从数据库中丢弃

问题2:如何实现逻辑删除?

逻辑删除:为数据设置是否可用状态字段,删除时设置状态字段为不可用状态,数据保留在数据库中

Mybatis和Mybatisplus_第12张图片

 

逻辑删除案例

①:数据库表中添加逻辑删除标记字段

Mybatis和Mybatisplus_第13张图片

 

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

import com.baomidou.mybatisplus.annotation.*;

import lombok.Data;

@Data
public class User {

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

③:配置逻辑删除字面值

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

逻辑删除本质:逻辑删除的本质其实是修改操作。如果加了逻辑删除字段,查询数据时也会自动带上逻辑删除字段。

Mybatis和Mybatisplus_第14张图片

  注意:如果不想自动带上逻辑删除字段,需要自己编写SQL。

 

4.4 乐观锁

4.4.1 【乐观锁】

问题1:加锁的场景?乐观锁使用场景?

业务并发现象带来的问题:秒杀

mp提供的乐观锁适用于小型的并发

问题2:mp乐观锁 实现流程和底层原理?

原理:update set xx=yy , version=version+1 where version=num;

mp通过拦截器进行增强

操作流程:

  1. 先查询version

  2. 修改操作(并发修改只有一个能成功,通过version的值来保证)

Mybatis和Mybatisplus_第15张图片

 

乐观锁案例

①:数据库表中添加锁标记字段

Mybatis和Mybatisplus_第16张图片

 

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

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.Version;
import lombok.Data;

@Data
public class User {

	private Long id;
	
    @Version
    private Integer version;

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

import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

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

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

④:使用乐观锁机制在修改前必须先获取到对应数据的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?条件还成立吗?
}

Mybatis和Mybatisplus_第17张图片

 

你可能感兴趣的:(mybatis,java,mysql)