mybatis-plus仿 JPA的 GenerationType.TABLE 实现

概述

1)配置mybatis-plus 主键默认策略,实体添加主键注解
2)新建主键存储表
3)编写主键策略实现类
注意事项:如果 程序含数据库恢复功能,数据恢复后,需调用 CustomIdGenerator.clearTabIdStateMap() 方法现在各个数据库都支持建表生成自增主键,这种方式性能挺好的;
mysql、postgrel、oracle 12 c +,sqlserver ;只是部分备份、恢复带了序列,没有屏蔽底层实现;根据实际情况选择实现方式,我们这边是因为测试变态:
说系统时间改了,程序不报错,所以默认mybatis-plus的雪花不让用;其次程序部分涉及跟时间相关业务,uuid主键实现效果很差;只能折中自己实现主键策略,本人更倾向于数据库策略:自增主键;

1、配置mybatis-plus 主键默认策略,实体添加主键注解

1) 配置mybatis-plus 主键默认策略

id-type: ASSIGN_ID,默认雪花,但被替换成自定义实现

# mybatis-plus
mybatis-plus:# log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl# log-impl: org.apache.ibatis.logging.stdout.StdOutImplmapper-locations: classpath*:mapper/*Mapper.xml,classpath*:mapper-mysql/*Mapper.xml#开启驼峰命名configuration:map-underscore-to-camel-case: truejdbc-type-for-null: 'null'global-config:banner: falsedb-config:id-type: ASSIGN_ID # 默认雪花,但被替换成自定义实现logic-delete-field: DELETE_FLAGlogic-delete-value: 1logic-not-delete-value: 0column-format: "`%s`"

2) 实体添加 主键注解:

主键字段添加 @TableId 注解,实体建议添加 @TableName

package entity;import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;import java.io.Serial;
import java.io.Serializable;/*** <p>* 白名单* </p>** @author fisec* @since 2024-04-24*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper=true)
@TableName("sys_ip_whitelist")
public class IpWhitelist implements Serializable {@Serialprivate static final long serialVersionUID = 1L;/*** 主键*/@TableIdprivate Long id;....}

2、新建主键存储表

用于存储主键的关系

CREATE TABLE `pk_gen` (`sequence_name` varchar(255) NOT NULL,`next_val` bigint DEFAULT NULL,PRIMARY KEY (`sequence_name`)
) ENGINE=InnoDB;

3、编写主键策略实现类

实际 包含4个类:

1、CustomIdGenerator.class :实现mybatis-plus主键接口
2、PkGenService.class:与存储数据库表交互
## 下面2个类为仿写 hibernate 实现
3、GenerationTableId.class:调用和存储生成的主键,对应 hibernate 的 PooledLoThreadLocalOptimizer.class
4、TableIdGenerator.class:调用数据交互 PkGenService,对应 hibernate 的 TableGenerator.class

1)CustomIdGenerator.class 实现mybatis-plus主键接口

实现 IdentifierGenerator 的 nextId方法,来调用主键生成类

package system.config.id;import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import system.pkGen.service.IPkGenService;
import org.springframework.stereotype.Component;/*** 替换默认ASSIGN_ID(默认的雪花算法),为自己的实现*/
@Component
public class CustomIdGenerator implements IdentifierGenerator {/*** 数据库表生成策略*/private final GenerationTableId generationTableId = new GenerationTableId();public void clearTabIdStateMap(){generationTableId.clearTabIdStateMap();}@Overridepublic Number nextId(Object entity) {// 1 可以将当前传入的class 获取对应的数据库表名String tableName = TableInfoHelper.getTableInfo(entity.getClass()).getTableName();// 2 表主键生成策略TableIdGenerator tableIdGenerator = new TableIdGenerator();tableIdGenerator.setTenantIdentifier(tableName);// 3 通过上下文获取IPkGenService,防止产生循环依赖IPkGenService iPkGenService = SpringUtil.getBean(IPkGenService.class);// 4 调用策略生成主键return generationTableId.generate(iPkGenService, tableIdGenerator);}
}

2)PkGenService.class 与存储数据库表交互

核心查询 FOR UPDATE ,保证分布式系统的主键唯一性,保证每个应用或线程取到 id段 唯一

package system.pkGen.service.impl;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fisec.system.pkGen.entity.PkGen;
import com.fisec.system.pkGen.mapper.PkGenMapper;
import com.fisec.system.pkGen.service.IPkGenService;
import org.springframework.stereotype.Service;/*** <p>* 服务实现类* </p>** @author fisec* @since 2024-07-08*/
@Service
public class PkGenServiceImpl extends ServiceImpl<PkGenMapper, PkGen> implements IPkGenService {@Overridepublic PkGen getForUpdateByName(String sequenceName) {LambdaQueryWrapper<PkGen> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(PkGen::getSequenceName,sequenceName);// 1、查询加锁,保证分布式和多线程中的唯一性queryWrapper.last("FOR UPDATE");return baseMapper.selectOne(queryWrapper);}
}

3) 主键生成核心类 - GenerationTableId.class

主键生成类包含2个: GenerationTableId.class 用于存储和调用生成主键,TableIdGenerator.class 数据库调用(调用核心查询 FOR UPDATE )

# 1)hibernate
对应 org.hibernate.id.enhanced.PooledLoThreadLocalOptimizer.class,与 JPA hibinate实现 区别
hibinate 是线程级别的,因为 hibinate 整个会话自己管理
ThreadLocal<GenerationState> singleTenantState = ThreadLocal.withInitial( GenerationState::new );
# 2)因为这个被调用是tomcate的线程级别
所以不推荐 使用 ThreadLocal ,这里改为了ConcurrentHashMap 应用级别;

注意:GenerationTableId类内的 incrementSize:取到的主键步长可以自定义,这边为了开发方便测试为1,正式为100,hibernate默认为50

package system.config.id;import cn.hutool.core.util.NumberUtil;
import com.fisec.common.global.FisecConfig;
import com.fisec.system.pkGen.service.IPkGenService;
import lombok.extern.slf4j.Slf4j;import java.util.concurrent.ConcurrentHashMap;@Slf4j
public class GenerationTableId {private final ConcurrentHashMap<String, GenerationState> tabIdStateMap = new ConcurrentHashMap<>();public void clearTabIdStateMap(){tabIdStateMap.clear();}/*** 加载当前线程的 GenerationState** @param tableName 表名* @return GenerationState*/private GenerationState locateGenerationState(String tableName) {GenerationState state = tabIdStateMap.get(tableName);if (state == null) {state = new GenerationState();tabIdStateMap.put(tableName, state);}return state;}/*** 调用生成主键的方法** @param tableIdGenerator 表主键生成策略* @return long*/public Long generate(IPkGenService iPkGenService, TableIdGenerator tableIdGenerator) {return locateGenerationState(tableIdGenerator.getTenantIdentifier()).generate(iPkGenService, tableIdGenerator, 50);}/*** 当前线程的表生成策略*/private static class GenerationState {/*** 上次从数据库取到的值*/private Long lastSourceValue;/*** 当前的值*/private Long value;/*** 更新主键的上限制*/private Long upperLimitValue;private Long generate(IPkGenService iPkGenService, TableIdGenerator tableIdGenerator, Long incrementSize) {if (value == null || value >= upperLimitValue) {lastSourceValue = tableIdGenerator.nextValue(iPkGenService, incrementSize);upperLimitValue = NumberUtil.add(lastSourceValue, incrementSize).longValue();value = lastSourceValue;}return value++;}}
}

4)主键生成核心类 - TableIdGenerator.class

TableIdGenerator.class :查询需要到的主键状态,并把下次生成的状态存储到数据库
对应 hibernate的 org.hibernate.id.enhanced.TableGenerator.class

package system.config.id;import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import com.fisec.system.pkGen.entity.PkGen;
import com.fisec.system.pkGen.service.IPkGenService;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;@Slf4j
@Getter
@Setter
public class TableIdGenerator {/*** 生成主键的票据*/private String tenantIdentifier;/*** 生成表主键的核心** @param iPkGenService 数据库连接回话* @return long*/public long nextValue(IPkGenService iPkGenService, Long incrementSize) {// 1 通过mybatis-plus 获取表名和列表String tableName = this.tenantIdentifier;final Long nextValue;try {PkGen pkGen = iPkGenService.getForUpdateByName(tableName);if (ObjectUtil.isNull(pkGen)) {nextValue = 1L;pkGen = new PkGen();pkGen.setSequenceName(tableName);pkGen.setNextVal(NumberUtil.add(nextValue, incrementSize).longValue());iPkGenService.save(pkGen);} else {nextValue = pkGen.getNextVal();pkGen.setNextVal(NumberUtil.add(nextValue, incrementSize).longValue());iPkGenService.updateById(pkGen);}return nextValue;} catch (Exception e) {throw new RuntimeException("Error executing SQL", e);}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/47482.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何使用Milvus Cloud进行稀疏向量搜索

如何使用Milvus Cloud进行向量搜索Milvus Cloud 是一款高度可扩展、性能出色的开源向量数据库。在最新的 2.4 版本中,Milvus Cloud 支持了稀疏和稠密向量(公测中)。本文将利用 Milvus Cloud 2.4 来存储数据集并执行向量搜索。 接下来,我们将演示如何利用 Milvus Cloud 在 M…

[GXYCTF2019]Ping Ping Ping1

打开靶机 结合题目名称&#xff0c;考虑是命令注入&#xff0c;试试ls 结果应该就在flag.php。尝试构造命令注入载荷。 cat flag.php 可以看到过滤了空格,用 $IFS$1替换空格 还过滤了flag&#xff0c;我们用字符拼接的方式看能否绕过,ag;cat$IFS$1fla$a.php。注意这里用分号间隔…

睡前故事—绿色科技的未来:可持续发展的梦幻故事

欢迎来到《Bedtime Stories Time》。这是一个我们倾听、放松、并逐渐入睡的播客。感谢你收听并支持我们&#xff0c;希望你能将这个播客作为你睡前例行活动的一部分。今晚我们将讲述绿色科技的未来&#xff1a;可持续发展的梦幻故事的故事。一个宁静的夜晚&#xff0c;希望你现…

0602STM32定时器输出比较

STM32定时器输出比较 PWM简介 主要用来输出PWM波形&#xff0c;PWM波形又是驱动电机的必要条件&#xff0c;所以如果想用STM32做一些有电机的项目&#xff0c;比如智能车&#xff0c;机器人等。那输出比较功能就要认真掌握 1.PWM驱动LED呼吸灯 2.PWM驱动舵机 3.PWM驱动直流电机…

vue中获取剪切板中的内容

目录 1.说明 2.示例 3.总结 1.说明 在系统中的画面或者时外部文件中进行拷贝处理后&#xff0c;在页面中可以获取剪切板的内容。 2.示例 方式①(直接获取) // 异步函数获取剪切板内容 async function getClipboardContent(ev: any) {try {ev.preventDefault()const clip…

搜维尔科技:【研究】触觉技术将在5年内以8种方式改变人们的世界

触觉技术在过去几年中发展迅猛&#xff0c;大大提高了反馈的精确度和真实度。其应用产生了真正的影响&#xff0c;数百家公司和企业都集成了触觉技术来增强培训和研究模拟。 虽然触觉技术主要用于 B2B 层面&#xff0c;但触觉技术可能会彻底改变我们的生活&#xff0c;尤其是通…

DLMS协议中的高级安全(HLS)身份验证

1.四步身份验证协议 在IEC 62056-53中已说明&#xff0c;ACSE提供部分高级身份安全&#xff08;HLS&#xff09;验证服务。高级身份安全验证适用于通信通道不能提供内部安全&#xff0c;应采取防范措施以防止偷听和信息&#xff08;密码&#xff09;重现的情况。这时&#xff…

视频共享融合赋能平台LntonCVS视频监控业务平台技术方案详细介绍

LntonCVS国标视频综合管理平台是一款智慧物联应用平台&#xff0c;核心技术基于视频流媒体&#xff0c;采用分布式和负载均衡技术开发&#xff0c;提供广泛兼容、安全可靠、开放共享的视频综合服务。该平台功能丰富&#xff0c;包括视频直播、录像、回放、检索、云存储、告警上…

【数据结构】详解堆

一、堆的概念 堆(Heap)是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵 完全二叉树的 数组对象。 堆是非线性数据结构&#xff0c;相当于一维数组&#xff0c;有两个直接后继。 如果有一个关键码的集合K { k₀&#xff0c;k₁&#xff0c;k₂ &#xff0…

数据结构(双向链表)

链表的分类 链表的结构⾮常多样&#xff0c;以下情况组合起来就有8种&#xff08;2 x 2 x 2&#xff09;链表结构&#xff1a; 虽然有这么多的链表的结构&#xff0c;但是我们实际中最常⽤还是两种结构&#xff1a;单链表和双向带头循环链表 1.⽆头单向⾮循环链表&#xff1a…

Kafka系列之:Kafka存储数据相关重要参数理解

Kafka系列之:Kafka存储数据相关重要参数理解 一、log.segment.bytes二、log.retention.bytes三、日志段四、log.retention.check.interval.ms五、数据底层文件六、index、log、snapshot、timeindex、leader-epoch-checkpoint、partition.metadata一、log.segment.bytes 参数lo…

第十课:telnet(远程登入)

如何远程管理网络设备&#xff1f; 只要保证PC和路由器的ip是互通的&#xff0c;那么PC就可以远程管理路由器&#xff08;用telnet技术管理&#xff09;。 我们搭建一个下面这样的简单的拓扑图进行介绍 首先我们点击云&#xff0c;把云打开&#xff0c;点击增加 我们绑定vmn…

Android 10.0 系统属性控制蓝牙分享功能打开和关闭功能实现

1.前言 在10.0的系统rom产品定制化开发中,在进行某些蓝牙模块开发中,原生系统中会默认蓝牙有分享功能,而在实际的项目中 要求去掉分享功能,所以接下来分析下相关功能实现 2.系统属性控制蓝牙分享功能打开和关闭功能实现的核心类 frameworks\base\core\java\android\app\…

【面试题】Redo log和Undo log

Redo log 介绍Redo log之前我们需要了解一下&#xff0c;mysql数据操作的流程&#xff1a; 上述就是数据操作的流程图&#xff0c;可以发现sql语句并不是直接操作的磁盘而是通过操作内存&#xff0c;然后进行内存到磁盘的一个同步。这里我们必须要了解一些区域&#xff1a; 缓…

华为HCIP Datacom H12-821 卷42

42.填空题 如图所示&#xff0c;MSTP网络中SW1为总根&#xff0c;请将以下交换机与IST域根和主桥配对。 参考答案&#xff1a;主桥1468 既是IST域根又是主桥468 既不是又不是就是25 解析&#xff1a; 主桥1468 既是IST域根又是主桥468 既不是又不是就是25 43.填空题 网络有…

[日进斗金系列]用码上飞解决企微开发维修管理系统的需求

前言&#xff1a; 今天跟大家唠唠如何用小money生 大money的方法&#xff0c;首先我们需要准备一个工具。 这个工具叫码上飞CodeFlying&#xff0c;它是目前国内首发的L4级自动化智能软件开发平台。 它可以在短时间内&#xff0c;与AI进行几轮对话就能开发出一个可以解决实际…

WEB前端06-BOM对象

BOM浏览器对象模型 浏览器对象模型&#xff1a;将浏览器的各个组成部分封装成对象。是用于描述浏览器中对象与对象之间层次关系的模型&#xff0c;提供了独立于页面内容、并能够与浏览器窗口进行交互的对象结构。 组成部分 Window&#xff1a;浏览器窗口对象 Navigator&…

MacOS命令行运行fortran程序|编程私教解答

你好&#xff0c;我是悦创。 Fortran 是一种经典的编程语言&#xff0c;广泛用于科学计算和工程领域。如果你使用 macOS 系统并希望在命令行环境中编译和运行 Fortran 程序&#xff0c;这篇教程将引导你完成整个过程。 1. 准备工作 首先&#xff0c;确保你的 macOS 系统已安…

【Android】传给后端的Url地址被转码问题处理

一、问题 为什么使用Gson().toJson的时候&#xff0c;字符串中的会被转成\u003d 在 Gson 中&#xff0c;默认情况下会对某些特殊字符进行 HTML 转义&#xff0c;以确保生成的 JSON 字符串在 HTML 中是安全的。因此&#xff0c;字符 会被转义为 \u003d。你可以通过禁用 HTML 转…

区块链资料

Quantstamp - Public Security Assessments smart-contract-sanctuary-bsc/contracts/mainnet at master tintinweb/smart-contract-sanctuary-bsc GitHub https://github.com/slowmist/Cryptocurrency-Security-Audit-Guide/blob/main/README_CN.md sFuzz: 高效自适应的智…