Mybatis-plus

文章目录

  • 一、拓展功能
    • 1.1 逻辑删除
      • 1.1.1 逻辑删除实现
    • 1.2 乐观锁与悲观锁
      • 1.2.1 介绍
      • 1.2.2 mybatis-plus实现版本号乐观锁
  • 二、mybatis-plus 注解
    • 2.1@TableName注解
    • 2.2 @TableId 注解
    • 2.3 @TableField
  • 三、分页插件
    • 3.1 mybatis-plus自带方法
    • 3.2 自定义方法实现分页


mybatis-plus总结:

自动生成单表的CRUD功能

提供丰富的条件拼接方式

全自动ORM类型持久层框架

一、拓展功能

1.1 逻辑删除

逻辑删除,可以方便地实现对数据库记录的逻辑删除而不是物理删除。逻辑删除是指通过更改记录的状态或添加标记字段来模拟删除操作,从而保留了删除前的数据,便于后续的数据分析和恢复。

  • 物理删除:真实删除,将对应数据从数据库中删除,之后查询不到此条被删除的数据
  • 逻辑删除:假删除,将对应数据中代表是否被删除字段的状态修改为“被删除状态”,之后在数据库中仍旧能看到此条数据记录

1.1.1 逻辑删除实现

1、数据库和实体类添加

a.表添加逻辑删除字段
可以是一个布尔类型、整数类型或枚举类型。

ALTER TABLE USER ADD deleted INT DEFAULT 0 ;  # int 类型 1 逻辑删除 0 未逻辑删除

b.实体类添加逻辑删除字段
方法1:单个指定

@Data
public class User {

   // @TableId
    private Integer id;
    private String name;
    private Integer age;
    private String email;
    
    @TableLogic
    //逻辑删除字段 int mybatis-plus下,默认 逻辑删除值为1 未逻辑删除 1 
    private Integer deleted;
}

方法2:全局指定

mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: deleted # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
      logic-delete-value: 1 # 逻辑已删除值(默认为 1)
      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)

2、测试:

//逻辑删除
@Test
public void testQuick5(){
    //逻辑删除
    userMapper.deleteById(5);
}
执行效果:

JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@5871a482] will not be managed by Spring
==> Preparing: UPDATE user SET deleted=1 WHERE id=? AND deleted=0
==> Parameters: 5(Integer)
<==    Updates: 1

可以发现:添加了逻辑删除之后,进行删除操作其实是更新操作,找到符合我们删除条件的,然后看其逻辑删除字段是否为0,这个默认值也可以修改。若是0就将其修改为1

1.2 乐观锁与悲观锁

1.2.1 介绍

解决思路:

乐观锁和悲观锁是在并发编程中用于处理并发访问和资源竞争的两种不同的锁机制!!

悲观锁:
悲观锁的基本思想是,在整个数据访问过程中,将共享资源锁定,以确保其他线程或进程不能同时访问和修改该资源。悲观锁的核心思想是"先保护,再修改"。在悲观锁的应用中,线程在访问共享资源之前会获取到锁,并在整个操作过程中保持锁的状态,阻塞其他线程的访问。只有当前线程完成操作后,才会释放锁,让其他线程继续操作资源。这种锁机制可以确保资源独占性和数据的一致性,但是在高并发环境下,悲观锁的效率相对较低。

乐观锁:
乐观锁的基本思想是,认为并发冲突的概率较低,因此不需要提前加锁,而是在数据更新阶段进行冲突检测和处理。乐观锁的核心思想是"先修改,后校验"。在乐观锁的应用中,线程在读取共享资源时不会加锁,而是记录特定的版本信息。当线程准备更新资源时,会先检查该资源的版本信息是否与之前读取的版本信息一致,如果一致则执行更新操作,否则说明有其他线程修改了该资源,需要进行相应的冲突处理。乐观锁通过避免加锁操作,提高了系统的并发性能和吞吐量,但是在并发冲突较为频繁的情况下,乐观锁会导致较多的冲突处理和重试操作。

理解点: 悲观锁和乐观锁是两种解决并发数据问题的思路,不是具体技术!!!
具体技术和方案:

  1. 乐观锁实现方案和技术:
    • 版本号/时间戳:为数据添加一个版本号或时间戳字段,每次更新数据时,比较当前版本号或时间戳与期望值是否一致,若一致则更新成功,否则表示数据已被修改,需要进行冲突处理。
    • CAS(Compare-and-Swap):使用原子操作比较当前值与旧值是否一致,若一致则进行更新操作,否则重新尝试。
    • 无锁数据结构:采用无锁数据结构,如无锁队列、无锁哈希表等,通过使用原子操作实现并发安全。
  2. 悲观锁实现方案和技术:
    • 锁机制:使用传统的锁机制,如互斥锁(Mutex Lock)或读写锁(Read-Write Lock)来保证对共享资源的独占访问。
    • 数据库锁:在数据库层面使用行级锁或表级锁来控制并发访问。
    • 信号量(Semaphore):使用信号量来限制对资源的并发访问。

介绍版本号乐观锁技术的实现流程:

  • 每条数据添加一个版本号字段version
  • 取出记录时,获取当前 version
  • 更新时,检查获取版本号是不是数据库当前最新版本号
  • 如果是[证明没有人修改数据], 执行更新, set 数据更新 , version = version+ 1
  • 如果 version 不对[证明有人已经修改了],我们现在的其他记录就是失效数据!就更新失败

1.2.2 mybatis-plus实现版本号乐观锁

在数据库中添加一列

ALTER TABLE USER ADD VERSION INT DEFAULT 1 ;  # int 类型 乐观锁字段

实体类中也要添加相应字段,并且添加上注解

@Version
private Integer version;

添加拦截器

@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
    return interceptor;
}

正常更新

//演示乐观锁生效场景
@Test
public void testQuick7(){
    //步骤1: 先查询,在更新 获取version数据
    //同时查询两条,但是version唯一,最后更新的失败
    User user  = userMapper.selectById(5);
    User user1  = userMapper.selectById(5);

    user.setAge(20);
    user1.setAge(30);

    userMapper.updateById(user);
    //乐观锁生效,失败!
    userMapper.updateById(user1);
}

最终结果只会更新20,因为在刚开始获取时user1和user 版本号均为1,在user更新完之后,version+1变为2,导致user1更新时发现自己刚开始获取的版本号与现在的版本号不同就会导致更新失败
更新:
如果这个样子写的话 就不会更新失败,因为user1的version获取,是在user更新之后的,获得的version是一致的

        User user  = userMapper.selectById(5);


        user.setAge(20);
    

        userMapper.updateById(user);
              User user1  = userMapper.selectById(5);
            user1.setAge(30);
        userMapper.updateById(user1);

二、mybatis-plus 注解

2.1@TableName注解

  • 描述:表名注解,标识实体类对应的表
  • 使用位置:实体类
@TableName("sys_user") //对应数据库表名
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

特殊情况:如果表名和实体类名相同(忽略大小写)可以省略该注解!

其他解决方案:全局设置前缀 (https://www.baomidou.com/pages/56bac0/#基本配置)

mybatis-plus: # mybatis-plus的配置
  global-config:
    db-config:
      table-prefix: sys_ # 表名前缀字符串

2.2 @TableId 注解

  • 描述:主键注解
  • 使用位置:实体类主键字段
- @TableName("sys_user")
public class User {
    @TableId(value="主键列名",type=主键策略)
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

Mybatis-plus_第1张图片

全局配置修改主键策略:

mybatis-plus:
  configuration:
    # 配置MyBatis日志
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config:
    db-config:
      # 配置MyBatis-Plus操作表的默认前缀
      table-prefix: t_
      # 配置MyBatis-Plus的主键策略
      id-type: auto

在以下场景下,添加@TableId注解是必要的:

  1. 实体类的字段与数据库表的主键字段不同名:如果实体类中的字段与数据库表的主键字段不一致,需要使用@TableId注解来指定实体类中表示主键的字段。
  2. 主键生成策略不是默认策略:如果需要使用除了默认主键生成策略以外的策略,也需要添加@TableId注解,并通过value属性指定生成策略。
    你需要记住的: 雪花算法生成的数字,需要使用Long 或者 String类型主键!!

注意:

type为AUTO时:是自增长,需要我们在设计表时就将主键设置为auto_incresment自增长。
type为assign_id时:默认使用雪花算法,注意主键类型要为Long或者String

2.3 @TableField

描述:字段注解(非主键)

@TableName("sys_user")
public class User {
    @TableId
    private Long id;
    @TableField("nickname")
    private String name;
    private Integer age;
    private String email;
}

属性 类型 必须指定 默认值 描述
value String 否 “” 数据库字段名
exist boolean 否 true 是否为数据库表字段

MyBatis-Plus会自动开启驼峰命名风格映射!!!

三、分页插件

3.1 mybatis-plus自带方法

添加分页插件

@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
    return interceptor;
}

进行分页查询

@Test
public void testPageQuery(){
    //设置分页参数
    Page page = new Page<>(1, 5);
    userMapper.selectPage(page, null);
    //获取分页数据
    List list = page.getRecords();
    list.forEach(System.out::println);
    System.out.println("当前页:"+page.getCurrent());
    System.out.println("每页显示的条数:"+page.getSize());
    System.out.println("总记录数:"+page.getTotal());
    System.out.println("总页数:"+page.getPages());
    System.out.println("是否有上一页:"+page.hasPrevious());
    System.out.println("是否有下一页:"+page.hasNext());
}

selectPage(Ipage,wapper)
Ipage 是一个接口,Page是其的一个实现。
Page<实体类对象> page = new Page<>(页码, 页容量);

3.2 自定义方法实现分页

UserMapper

package com.cky.mapper;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.cky.pojo.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;

/**
* @author 10945
* @description 针对表【news_user】的数据库操作Mapper
* @createDate 2023-12-10 11:24:32
* @Entity com.cky.pojo.User
*/
public interface UserMapper extends BaseMapper {
    IPage selectByage(IPage page, @Param("uid") Integer uid);
}

UserMapper.xml
返回值类型是泛型的类型

    

Test

   @Test
    public void test2(){
        Page page=new Page<>(1,5);
        userMapper.selectByage(page,9);
        System.out.println(page.getRecords());

    }

你可能感兴趣的:(mybatis,mybatis,oracle,数据库)