位置: IT常识 - 正文

防止数据重复提交的6种方法(超简单)!(数据库防止重复数据)

发布时间:2024-01-16
防止数据重复提交的6种方法(超简单)!

推荐整理分享防止数据重复提交的6种方法(超简单)!(数据库防止重复数据),希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:防止数据重复excel,防止数据重复提交,防止数据重复输入的正确公式为,避免数据重复,防止数据重复提示的方法,防止数据重复提交,防止数据重复提示的方法,防止数据重复提示的方法,内容如对您有帮助,希望把文章链接给更多的朋友!

有位朋友,某天突然问磊哥:在 Java 中,防止重复提交最简单的方案是什么?

这句话中包含了两个关键信息,第一:防止重复提交;第二:最简单。

于是磊哥问他,是单机环境还是分布式环境?

得到的反馈是单机环境,那就简单了,于是磊哥就开始装*了。

话不多说,我们先来复现这个问题。

模拟用户场景

根据朋友的反馈,大致的场景是这样的,如下图所示:

简化的模拟代码如下(基于 Spring Boot):

import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;@RequestMapping("/user")@RestControllerpublic class UserController {/*** 被重复请求的方法*/@RequestMapping("/add")public String addUser(String id) {// 业务代码...System.out.println("添加用户ID:" + id);return "执行成功!";}}复制代码

于是磊哥就想到:通过前、后端分别拦截的方式来解决数据重复提交的问题。

前端拦截

前端拦截是指通过 HTML 页面来拦截重复请求,比如在用户点击完“提交”按钮后,我们可以把按钮设置为不可用或者隐藏状态。

防止数据重复提交的6种方法(超简单)!(数据库防止重复数据)

执行效果如下图所示:

前端拦截的实现代码:

<html><script>function subCli(){// 按钮设置为不可用document.getElementById("btn_sub").disabled="disabled";document.getElementById("dv1").innerText = "按钮被点击了~";}</script><body style="margin-top: 100px;margin-left: 100px;"><input id="btn_sub" type="button" value=" 提 交 " onclick="subCli()"><div id="dv1" style="margin-top: 80px;"></div></body></html>复制代码

但前端拦截有一个致命的问题,如果是懂行的程序员或非法用户可以直接绕过前端页面,通过模拟请求来重复提交请求,比如充值了 100 元,重复提交了 10 次变成了 1000 元(瞬间发现了一个致富的好办法)。

所以除了前端拦截一部分正常的误操作之外,后端的拦截也是必不可少。

后端拦截

后端拦截的实现思路是在方法执行之前,先判断此业务是否已经执行过,如果执行过则不再执行,否则就正常执行。

我们将请求的业务 ID 存储在内存中,并且通过添加互斥锁来保证多线程下的程序执行安全,大体实现思路如下图所示:

然而,将数据存储在内存中,最简单的方法就是使用 HashMap 存储,或者是使用 Guava Cache 也是同样的效果,但很显然 HashMap 可以更快的实现功能,所以我们先来实现一个 HashMap 的防重(防止重复)版本。

1.基础版——HashMapimport org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import java.util.HashMap;import java.util.Map;/*** 普通 Map 版本*/@RequestMapping("/user")@RestControllerpublic class UserController3 {// 缓存 ID 集合private Map<String, Integer> reqCache = new HashMap<>();@RequestMapping("/add")public String addUser(String id) {// 非空判断(忽略)...synchronized (this.getClass()) {// 重复请求判断if (reqCache.containsKey(id)) {// 重复请求System.out.println("请勿重复提交!!!" + id);return "执行失败";}// 存储请求 IDreqCache.put(id, 1);}// 业务代码...System.out.println("添加用户ID:" + id);return "执行成功!";}}复制代码

实现效果如下图所示:

存在的问题:此实现方式有一个致命的问题,因为 HashMap 是无限增长的,因此它会占用越来越多的内存,并且随着 HashMap 数量的增加查找的速度也会降低,所以我们需要实现一个可以自动“清除”过期数据的实现方案。

2.优化版——固定大小的数组

此版本解决了 HashMap 无限增长的问题,它使用数组加下标计数器(reqCacheCounter)的方式,实现了固定数组的循环存储。

当数组存储到最后一位时,将数组的存储下标设置 0,再从头开始存储数据,实现代码如下:

import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import java.util.Arrays;@RequestMapping("/user")@RestControllerpublic class UserController {private static String[] reqCache = new String[100]; // 请求 ID 存储集合private static Integer reqCacheCounter = 0; // 请求计数器(指示 ID 存储的位置)@RequestMapping("/add")public String addUser(String id) {// 非空判断(忽略)...synchronized (this.getClass()) {// 重复请求判断if (Arrays.asList(reqCache).contains(id)) {// 重复请求System.out.println("请勿重复提交!!!" + id);return "执行失败";}// 记录请求 IDif (reqCacheCounter >= reqCache.length) reqCacheCounter = 0; // 重置计数器reqCache[reqCacheCounter] = id; // 将 ID 保存到缓存reqCacheCounter++; // 下标往后移一位}// 业务代码...System.out.println("添加用户ID:" + id);return "执行成功!";}}复制代码3.扩展版——双重检测锁(DCL)

上一种实现方法将判断和添加业务,都放入 synchronized 中进行加锁操作,这样显然性能不是很高,于是我们可以使用单例中著名的 DCL(Double Checked Locking,双重检测锁)来优化代码的执行效率,实现代码如下:

import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import java.util.Arrays;@RequestMapping("/user")@RestControllerpublic class UserController {private static String[] reqCache = new String[100]; // 请求 ID 存储集合private static Integer reqCacheCounter = 0; // 请求计数器(指示 ID 存储的位置)@RequestMapping("/add")public String addUser(String id) {// 非空判断(忽略)...// 重复请求判断if (Arrays.asList(reqCache).contains(id)) {// 重复请求System.out.println("请勿重复提交!!!" + id);return "执行失败";}synchronized (this.getClass()) {// 双重检查锁(DCL,double checked locking)提高程序的执行效率if (Arrays.asList(reqCache).contains(id)) {// 重复请求System.out.println("请勿重复提交!!!" + id);return "执行失败";}// 记录请求 IDif (reqCacheCounter >= reqCache.length) reqCacheCounter = 0; // 重置计数器reqCache[reqCacheCounter] = id; // 将 ID 保存到缓存reqCacheCounter++; // 下标往后移一位}// 业务代码...System.out.println("添加用户ID:" + id);return "执行成功!";}}复制代码

注意:DCL 适用于重复提交频繁比较高的业务场景,对于相反的业务场景下 DCL 并不适用。

4.完善版——LRUMap

上面的代码基本已经实现了重复数据的拦截,但显然不够简洁和优雅,比如下标计数器的

本文链接地址:https://www.jiuchutong.com/zhishi/297477.html 转载请保留说明!

上一篇:一篇文章让你了解ADAS-HIL测试方案(一篇文章让你了解什么)

下一篇:HTML零基础入门教程完整版(html零基础入门教程)

  • 荣耀v7pro平板有高刷吗(荣耀v7pro平板有没有指纹)

    荣耀v7pro平板有高刷吗(荣耀v7pro平板有没有指纹)

  • 抖音慢放特效怎么操作(抖音慢放特效怎么弄)

    抖音慢放特效怎么操作(抖音慢放特效怎么弄)

  • ipad访问限制密码初始是多少(iPad访问限制密码忘了)

    ipad访问限制密码初始是多少(iPad访问限制密码忘了)

  • 微视在哪取消手机绑定(微视不用了怎么取消掉)

    微视在哪取消手机绑定(微视不用了怎么取消掉)

  • 华为录音机怎么设置(华为录音机怎么裁剪录音)

    华为录音机怎么设置(华为录音机怎么裁剪录音)

  • 订单编号可以查到什么(订单编号可以查别人买了什么吗)

    订单编号可以查到什么(订单编号可以查别人买了什么吗)

  • 苹果x突然掉电厉害(苹果手机突然掉电很快)

    苹果x突然掉电厉害(苹果手机突然掉电很快)

  • 苹果8p有原彩显示吗(8p的原彩)

    苹果8p有原彩显示吗(8p的原彩)

  • 工作表窗口冻结包括什么(工作表窗口冻结怎么设置)

    工作表窗口冻结包括什么(工作表窗口冻结怎么设置)

  • 一般压缩文件是什么格式(一般压缩文件是多少兆)

    一般压缩文件是什么格式(一般压缩文件是多少兆)

  • 充电宝充不上电一直闪(充电宝充不上电了但是灯还亮)

    充电宝充不上电一直闪(充电宝充不上电了但是灯还亮)

  • 乐播投屏到电视上为什么没有声音(乐播投屏到电视上不能显示全屏)

    乐播投屏到电视上为什么没有声音(乐播投屏到电视上不能显示全屏)

  • 剪切板是什么中的一个临时存储区(剪切板是什么中的)

    剪切板是什么中的一个临时存储区(剪切板是什么中的)

  • 苹果手机播客可以关闭吗(苹果手机播客可以定时关闭吗)

    苹果手机播客可以关闭吗(苹果手机播客可以定时关闭吗)

  • 苹果8怎么横屏设置(苹果8怎么横屏使用)

    苹果8怎么横屏设置(苹果8怎么横屏使用)

  • 苹果xsmax自带什么耳机(苹果xs max自带哪些配件)

    苹果xsmax自带什么耳机(苹果xs max自带哪些配件)

  • 小米蓝牙耳机可以控制音乐播放吗(小米蓝牙耳机可以连接华为手机吗)

    小米蓝牙耳机可以控制音乐播放吗(小米蓝牙耳机可以连接华为手机吗)

  • 无可用注册账号是什么(无可用注册账号怎么办)

    无可用注册账号是什么(无可用注册账号怎么办)

  • 钉钉审批怎么抄送(钉钉怎么制定审批流程)

    钉钉审批怎么抄送(钉钉怎么制定审批流程)

  • 荣耀20pro有人脸识别吗(荣耀20的人脸识别是什么类型的)

    荣耀20pro有人脸识别吗(荣耀20的人脸识别是什么类型的)

  • 苹果钥匙串在哪里设置(苹果钥匙串在哪里关闭)

    苹果钥匙串在哪里设置(苹果钥匙串在哪里关闭)

  • mac系统怎么删除Launchpad中的应用图标?(mac系统怎么删除用户)

    mac系统怎么删除Launchpad中的应用图标?(mac系统怎么删除用户)

  • ghost分区丢失只剩c盘(ghost后分区没有了)

    ghost分区丢失只剩c盘(ghost后分区没有了)

  • MAC系统中如何隐藏Dock上的程序图标(mac怎么隐藏app)

    MAC系统中如何隐藏Dock上的程序图标(mac怎么隐藏app)

  • 房地产小规模纳税人预缴税款如何填申报表
  • 财产转让所得的纳税义务发生时间
  • 税控盘会计处理
  • 房产契税新政策消息2023
  • 确认主营业务成本的依据是什么单据
  • 广告服务怎么做分录
  • 未分配利润转增股本需要交税吗
  • 专用发票包括增值税吗
  • 企业出售房屋交什么税
  • 红字发票开具只能针对一份发票 不可以只冲红其中一部分吗?
  • 最近偷税女星都有谁
  • 利润表净利润等于科目余额表
  • 公积金是交当月还是上月
  • 老板在外地怎么辞职
  • 两公司之间内部转账的会计处理
  • 软件开发开增票几个点
  • 土地增值税地价扣除
  • 延期申报出口退税流程
  • 小规模未达起征点附加税要计提吗
  • 怎么使用Windows安装器安装Windows10
  • windows在cmd设置变量
  • 环境保护税入账什么会计科目
  • 购买电脑应该怎么做账
  • linux如何使用
  • win切换平板模式
  • 代开专用发票缴纳增值税需要计提吗?
  • aliwssv.exe是什么进程
  • 政府机关出租房屋要交税吗
  • 增值税的特殊项目
  • 企业之间是什么意思
  • 待报解预算收入是社保扣费吗
  • 工商年报经营现金净流量计算公式
  • php递归函数详解
  • 细说php
  • 前端解决跨域问题的8种方案(最新最全)
  • 所得税申报表应怎么填
  • [Vue warn]: Error in render: “TypeError: Cannot read properties of undefined(reading“category1Name“
  • 加工行业增值税负率一般控制在多少?
  • 利润表增加利润怎么做
  • 劳务派遣申报表附表一怎么填
  • 分公司是独立的诉讼主体吗
  • 文件柜质量标准
  • 从厂家直接拿货需要什么条件
  • 购入需安装设备的会计分录
  • 企业的营业税金怎么计算
  • 债券溢价什么意思
  • 业务招待费文件
  • 公司注销未认证怎么处理
  • 手工凭证模板
  • 工会经费计提比例是2%还是0.8%
  • 企业对于发出的货物
  • 企业财务会计是应用在各类企业
  • 建账的基本原则包括
  • mysql5.7解压版安装步骤
  • sql实用教程
  • XP系统怎么删除密码
  • 获取linux系统版本
  • reald是什么格式
  • window10突然激活失效
  • linux充当虚拟内存的是哪个分区
  • windows8中文版是什么版本
  • linux定时任务详解
  • android打包v1v2
  • 如何短时间内学会打字
  • vxlan配置实例详解
  • linux中crontab的用法
  • javascript:void(0)的作用示例介绍
  • 一个简单的合并单元
  • node.js 环境配置
  • javascript常用函数大全
  • javascript数组有哪些方法
  • js easyui
  • javascript 函数
  • easyui加载数据慢
  • android入门教程
  • android 引用第三方库
  • 特岗教师服务期满证书
  • 法人变更税务登记表怎么填
  • 做好绩效管理工作税务局
  • 税务局监督执纪存在问题
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号