位置: IT常识 - 正文

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

编辑:rootadmin
防止数据重复提交的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零基础入门教程)

  • 微信状态可以看访客吗(微信状态可以看到浏览记录吗)

    微信状态可以看访客吗(微信状态可以看到浏览记录吗)

  • apple pencil怎么连接ipad(apple pencil怎么连接平板)

    apple pencil怎么连接ipad(apple pencil怎么连接平板)

  • 支付宝语音播报怎么关(支付宝语音播报怎么开启)

    支付宝语音播报怎么关(支付宝语音播报怎么开启)

  • 应用分身是什么意思呢(应用分身是什么时候开始的软件)

    应用分身是什么意思呢(应用分身是什么时候开始的软件)

  • 小米10息屏显示不显示(小米10息屏显示发绿光)

    小米10息屏显示不显示(小米10息屏显示发绿光)

  • 电脑主板坏了能修吗(电脑主板坏了能保修吗)

    电脑主板坏了能修吗(电脑主板坏了能保修吗)

  • 闲鱼限流后还能恢复流量吗(闲鱼限流有什么现象)

    闲鱼限流后还能恢复流量吗(闲鱼限流有什么现象)

  • 直播打赏是什么意思(直播打赏是什么人)

    直播打赏是什么意思(直播打赏是什么人)

  • 快手蓝V和红V的区别(快手蓝v和红v的关系)

    快手蓝V和红V的区别(快手蓝v和红v的关系)

  • oppo和realme系统一样吗(oppo系统和realme系统)

    oppo和realme系统一样吗(oppo系统和realme系统)

  • long和int区别(long int与int有区别吗)

    long和int区别(long int与int有区别吗)

  • 抖音评论黑色背景怎么弄(抖音评论黑色背景图)

    抖音评论黑色背景怎么弄(抖音评论黑色背景图)

  • vivox27有nfc功能吗(vivox27有无nfc功能)

    vivox27有nfc功能吗(vivox27有无nfc功能)

  • 怎样领取微信收钱码(怎样领取微信收款的器具)

    怎样领取微信收钱码(怎样领取微信收款的器具)

  • 小米9pro5g是双模吗(小米9pro5g单模)

    小米9pro5g是双模吗(小米9pro5g单模)

  • 华为nove5i可以双击亮屏吗(华为nova5i可以设置双系统吗)

    华为nove5i可以双击亮屏吗(华为nova5i可以设置双系统吗)

  • 淘宝店怎么上传图片(淘宝店怎么上传资质)

    淘宝店怎么上传图片(淘宝店怎么上传资质)

  • vivo手机上hd1什么意思(vivo手机的hd是什么意思)

    vivo手机上hd1什么意思(vivo手机的hd是什么意思)

  • 苹果8黑色和深空灰对比(苹果8黑色和深空灰那个)

    苹果8黑色和深空灰对比(苹果8黑色和深空灰那个)

  • iwatch怎么单方面解绑(苹果手表怎样单独使用)

    iwatch怎么单方面解绑(苹果手表怎样单独使用)

  • iphone x的信任在哪里(iphonexs信任)

    iphone x的信任在哪里(iphonexs信任)

  • 小米8怎么隐藏视频(小米8怎么隐藏热点)

    小米8怎么隐藏视频(小米8怎么隐藏热点)

  • 一体机开机黑屏怎么办(donview一体机开机黑屏)

    一体机开机黑屏怎么办(donview一体机开机黑屏)

  • 苹果xr怎么关后台(苹果xr怎么关后置摄像头)

    苹果xr怎么关后台(苹果xr怎么关后置摄像头)

  • 电脑驱动器在哪里打开(电脑驱动器哪里打开)

    电脑驱动器在哪里打开(电脑驱动器哪里打开)

  • 微信如何群发(微信如何群发好友)

    微信如何群发(微信如何群发好友)

  • 苹果mac机上安装windows 8系统的方法(图文教程)(在mac上安装ios应用)

    苹果mac机上安装windows 8系统的方法(图文教程)(在mac上安装ios应用)

  • 出口退税工作流程及内容
  • 财务软件回收
  • 新公司开基本户银行选择
  • 差额增税可以抵扣吗
  • 个人承担的社保计入管理费用吗
  • 发票领回来了怎么读入发票
  • 提供劳务企业所得税纳税义务发生时间
  • 出口无法收汇怎么办
  • 商业折扣的纳税影响
  • 子女教育专项附加扣除是什么意思
  • 人力资源代缴社保合法吗
  • 总分包外地施工怎样预缴税款?
  • 原材料保险公司赔偿会计分录怎么写
  • 以前年度取得假发票账务处理
  • 漏提折旧可以所得税扣除吗
  • 新成立公司注资流程
  • 个人所得税退税多久到账
  • 单位旧电脑处置
  • 金融业营业税改增值税
  • 房地产企业土地增值税清算案例
  • 原材料增值税怎么算
  • 税款所属期是什么意思举例子
  • 电子税务局如何查进项发票
  • 应收账款坏账收回会计处理
  • 出口企业预申报没有增值税专用发票稽核信息如何处理?
  • 购买方取得专票会计分录
  • 企业出租商铺交什么税费呢
  • 股东变更交印花税会计分录
  • 什么是会计凭证?会计凭证有哪些作用?
  • windows 10如何使用
  • bios咋进入
  • 固定资产残值率计算公式
  • 报废资产未及时核销
  • 付佣金怎么做分录
  • 建筑公司分包
  • 2023年电子发票如何申领
  • php中提交表单数据
  • php源码怎么上传到服务器
  • 普通发票主营业务收入销项负数发票怎么做账
  • 一借多贷的会计分录怎么写
  • laccadive islands
  • 好奇地看着我
  • 其他营业账簿印花税减免政策
  • nginx搭建静态资源服务器
  • json解析漏洞
  • php代理访问
  • 蔬菜和肉类是不是免税
  • 一般纳税人公司出售旧车怎么开票
  • 建筑企业预缴税率
  • 其他综合收益的税后净额怎么计算
  • 金税四期的政策
  • 固定资产一次性扣除政策2023
  • 投资房地产的后续计量有哪几种模式
  • 申报残保金的时候有处罚决定书怎么办
  • 收到上年度企业所得税退税怎么做账
  • 未实际收到的投资收益
  • 颁发数字证书要符合什么条件
  • 如何使用命令查找电脑IP地址
  • 微软的定时炸弹就是Windows XP
  • linux快捷命令
  • Remind_XP.exe - Remind_XP是什么进程 有什么用
  • win7 64位系统只有搜狗浏览器可以打开网页其他浏览器打不开的故障原因及解决方法
  • jetcar.exe - jetcar是什么进程 有什么作用
  • windows对高分屏怎么这么差
  • win8的控制面板在哪里打开
  • cocos creator js ts
  • javascript语言基础
  • forfiles命令详解
  • perl编程
  • 在动画制作中一般默认帧数选择为
  • centos 开机启动程序
  • vuerouter嵌套路由
  • document.all.value
  • angularjs4
  • 怎样用python
  • 滴滴排队机制怎么设置
  • 代扣社保代扣公式是什么
  • 电脑多窗口同步操作
  • 增值税税控开票软件
  • 如果企业所得税没有做计提怎么办?
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设