首页 > 其他分享 >MyBatisPlus 之四:MP 的乐观锁和逻辑删除、分组、排序、链式的实现步骤

MyBatisPlus 之四:MP 的乐观锁和逻辑删除、分组、排序、链式的实现步骤

时间:2024-03-18 10:31:55浏览次数:27  
标签:MyBatisPlus version 删除 author price book MP 之四 import

乐观锁

乐观锁是相对悲观锁而言的,乐观锁假设数据一般情况不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果冲突,则返回给用户异常信息,让用户决定如何去做。

乐观锁适用于读多写少的场景,这样可以提高程序的吞吐量。

乐观锁采取了更加宽松的加锁机制。也是为了避免数据库幻读、业务处理时间过长等原因引起数据处理错误的一种机制,但乐观锁不会刻意使用数据库本身的锁机制,而是依据数据本身来保证数据的正确性。

乐观锁的实现:

  1. CAS 实现:Java 中java.util.concurrent.atomic包下面的原子变量使用了乐观锁的一种 CAS 实现方式。
  2. 版本号控制:一般是在数据表中加上一个数据版本号 version 字段,表示数据被修改的次数。当数据被修改时,version 值会 +1。当线程 A 要更新数据时,在读取数据的同时也会读取 version 值,在提交更新时,若刚才读取到的 version 值与当前数据库中的 version 值相等时才更新,否则重试更新操作,直到更新成功。

2️⃣说明 乐观并发控制相信事务之间的数据竞争(data race)的概率是比较小的,因此尽可能直接做下去,直到提交的时候才去锁定,所以不会产生任何锁和死锁。

MP 中乐观锁的实现

官网描述

OptimisticLockerInnerInterceptor 是 MyBatis Plus 提供的一个内置拦截器,用于实现乐观锁机制。

在并发环境下,乐观锁可以有效防止因并发更新导致的数据不一致问题

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

  • 取出记录时,获取当前version

  • 更新时,带上这个version

  • 执行更新时, set version = newVersion where version = oldVersion

  • 如果version不对,就更新失败

实现步骤:

a. 配置插件

package com.wdzl.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.pagination.optimize.JsqlParserCountOptimize;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

//Spring boot方式
@Configuration
public class MybatisPlusConfig {
    /**
     * 新版
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusLockInterceptor() {
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        // OptimisticLockerInnerInterceptor 是 MyBatis Plus 提供的一个内置拦截器
        mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return mybatisPlusInterceptor;
    }
}

b. 修改表结构

增加一列,用来表示记录的版本。类型必须是数值型的

create table book(
    bookid int primary key,
    bookname varchar(200),
    price float,
    pubdate datetime,
    author varchar(20),
    version int  
);

说明:

  • 支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime
  • 整数类型下 newVersion = oldVersion + 1
  • newVersion 会回写到 entity 中
  • 仅支持 updateById(id) 与 update(entity, wrapper) 方法
  • 在 update(entity, wrapper) 方法下, wrapper 不能复用!!!

c. 修改实体类

增加版本属性,并使用注解 @Vesion 标注。

注意:属性名命名没有要求。

@Version
private Integer version;

d. 测试

    /**
     * UPDATE book SET bookName=?, price=?, pubdate=?, author=?, version=? WHERE bookid=?             AND version=?
     */
    @Test
    public void lock(){
        Book book = bookDao.selectById(22);
        book.setPrice(book.getPrice()-5);


        Book book2 = bookDao.selectById(22);
        book2.setPrice(book.getPrice()-60);

           bookDao.updateById(book); //按主键修改

        //修改失败
        bookDao.updateById(book2);

    }
}

注意:配置好乐观锁插件和实体类注解后,再次修改,发现 SQL 语句的变化。添加了对于 version 版本字段的添加和修改

UPDATE book SET bookName=?, price=?, pubdate=?, author=?, version=? WHERE bookid=? AND version=?

逻辑删除

逻辑删除是数据库管理中的一种策略,它并不真正从数据库物理层面删除数据,而是通过更新数据表中的某个字段来标记这条记录为“已删除”状态。这样做的好处是可以保留历史数据,避免因误删导致的数据丢失,并且在需要时可以查询到被逻辑删除的记录。

在 MyBatis Plus 中,逻辑删除功能已经内置并提供了便捷的实现方式

官网说明:

说明:

只对自动注入的sql起效:

  • 插入: 不作限制
  • 查找: 追加where条件过滤掉已删除数据,且使用 wrapper.entity 生成的where条件会忽略该字段
  • 更新: 追加where条件防止更新到已删除数据,且使用 wrapper.entity 生成的where条件会忽略该字段
  • 删除: 转变为 更新

例如:

  • 删除: update user set deleted=1 where id = 1 and deleted=0
  • 查找: select id,name,deleted from user where deleted=0
实现步骤:

a) 修改表添加字段

表中添加一个列,用来表示删除状态的列

create table book(
    bookid int primary key,
    bookname varchar(200),
    price float,
    pubdate datetime,
    author varchar(20),
    version int,
    deleted int
);

b) 对应修改实体类,增加状态属性。

实体类字段上加上@TableLogic注解

@TableLogic
private Integer deleted;

c) 提前规定好删除标志的值和正常的值

可以在application.yml 配置状态值

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)

d) 测试:

    @Test
    void logicdel(){
        //SELECT bookid,bookName,price,pubdate,author,version,state FROM book WHERE state=0
//        List<Book> list = bookDao.selectList(null);

        //删除时 UPDATE book SET state=1 WHERE bookid=? AND state=0
        bookDao.deleteById(27);
    }

注意:在做select 查询,update 修改,删除操作时,执行的 SQL 都会有所变化。结合version来操作

分组

实现要求:

-- 按作者分组,查询每个作者初版的书数量及平均价格
SELECT author, AVG(price) 平均价格,COUNT(*) 数量
FROM book
WHERE state=0
GROUP BY author
HAVING COUNT(*)>2

代码实现:

void group(){
    QueryWrapper<Book> queryWrapper = new QueryWrapper<>();
    queryWrapper.select("author","count(*) bookCount","avg(price) pavg")
            .groupBy("author")
            .having("count(*)>2");
    List<Map<String, Object>> maps = bookDao.selectMaps(queryWrapper);
    for (Map<String, Object> map : maps) {
        String author = (String)map.get("author");
        Long count  = (Long)map.get("bookCount");
        Double pavg = (Double)map.get("pavg");
        System.out.println(author+"==="+count+"==="+pavg);
    }
}

排序、或、链式编程

/**
     * order by
     */
    public void order(){
        QueryWrapper<Book> queryWrapper = new QueryWrapper<>();
//        queryWrapper.orderByAsc("price");
        queryWrapper
                .orderByDesc("price")
                .orderByAsc("pubDate")
                .le("price",34)
                .or()
                .gt("pubDate",new Date())
                 .and(t->t.notIn("bookid",34,45,56)) 
                .gt("price",56);

        bookDao.selectList(queryWrapper);
 

 

标签:MyBatisPlus,version,删除,author,price,book,MP,之四,import
From: https://blog.csdn.net/zp8126/article/details/136798550

相关文章

  • Vue3之Composables
    前言Composables 称之为可组合项,熟悉 react 的同学喜欢称之为 hooks ,由于可组合项的存在,Vue3 中的组件之间共享状态比以往任何时候都更容易。这种新范例引入了一种更有组织性和可扩展性的方式来管理整个应用程序的状态和逻辑。什么是Composables本质上,可组合项是一种模式......
  • Qt/C++监控推流设备推流/延迟极低/实时性极高/rtsp/rtmp推流/hls/flv/webrtc拉流/调整
    一、前言算下来这个推流的项目作品写了有四年多了,最初第一个版本只有文件点播的功能,用的纯QTcpSocket通信实现,属于比较简单的功能。由于文件点播只支持文件形式的推流,不支持网络流或者本地设备采集,所以迫切需要打破这个瓶颈,而后加入核心的网络推流功能,这也是本项目的核心功能,不仅......
  • 数据结构(五)kmp---以题为例
    给定一个字符串 S,以及一个模式串 P,所有字符串中只包含大小写英文字母以及阿拉伯数字。模式串 P 在字符串 S 中多次作为子串出现。求出模式串 P 在字符串 S 中所有出现的位置的起始下标。输入格式第一行输入整数 N,表示字符串 P 的长度。第二行输入字符串 P。第......
  • VUE项目忽略ResizeObserver loop completed with undelivered notifications错误
    忽略"ResizeObserverlooplimitexceeded"和"ResizeObserverloopcompletedwithundeliverednotifications."两种错误。向vue.config.js中添加以下代码:module.exports=defineConfig({...devServer:{client:{overlay:{warnings:fa......
  • 【Python/Numpy】list/tuple/dictionary/numpy 的操作
    CommonDataStructuresListsListsaremutablearrays.普通操作#Twowaystocreateanemptylistempty_list=[]empty_list=list()#Createalistthatcontainsdifferentdatatypes,thisisallowedinPythonmylist=["aa","bb",1,2......
  • Java String类的compareTo() 方法
    compareTo()方法用于两种方式的比较:字符串与对象进行比较。按字典顺序比较两个字符串。intcompareTo(Objecto)或intcompareTo(StringanotherString)参数o--要比较的对象。anotherString--要比较的字符串。返回值返回值是整型,它是先比较对应字符的大小(ASC......
  • ffmpeg实现视频的加密解密
    刚打开一个视频,发现居然是加密的,感觉这个还挺有意思的:然后店家还给了这样一个播放的软件,打开是这样的:看来是需要密码的,从potplayer的打印来看就是这个视频的一些类型不对了,因此设备无法识别,就没法打开,我发现ffmpeg也可以实现:加密方式:#使用AES-128-CBC算法对视频文件进行加......
  • FFmpeg-aac、h264封装flv及时间转换
    文章目录时间概念流程api核心代码时间概念dts:解码时间戳,表示压缩帧的解码时间pts:显示时间戳,表示将压缩帧解码后得到的原始帧的显示时间时间基:time_base,通常以ms为单位时间戳:timestamp,多少个时间基真实时间:time_base*timestamp如一个视频帧......
  • Python - 安装依赖包,发现与其他包版本冲突 ResolutionImpossible
    问题表现Tofixthisyoucouldtryto:1.loosentherangeofpackageversionsyou'vespecified2.removepackageversionstoallowpipattempttosolvethedependencyconflictERROR:ResolutionImpossibleERROR:Cannotinstalltensorboard==1.10.0,tens......
  • ABC 345 F - Many Lamps
    ABC345F-ManyLamps解题思路:每次选取一条边,要么亮两个,要么灭两个,要么一灭一暗。亮的个数的奇偶性不变,所以不可能亮奇数个。考虑每个连通块。如果是偶数个一定能全亮,奇数个则最少一个不亮。对于两暗的,需要时通过操作点亮是一定的。考虑一明一暗时是加入边的操作意味什么:......