位置: 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零基础入门教程)

  • opporeno7支持无线充电功能吗(opporeno7支持无线充电吗)

    opporeno7支持无线充电功能吗(opporeno7支持无线充电吗)

  • b站app字体大小可以设置吗(b站新版本字体太小)

    b站app字体大小可以设置吗(b站新版本字体太小)

  • oppok5是不是5g网络手机(oppok5支不支持5g)

    oppok5是不是5g网络手机(oppok5支不支持5g)

  • opporeno4pro闪存是多少(opporeno5pro的闪存是什么)

    opporeno4pro闪存是多少(opporeno5pro的闪存是什么)

  • 在word2010中为图片设置文字环绕方式时(在word2010中使用图形图片,说法错误的是)

    在word2010中为图片设置文字环绕方式时(在word2010中使用图形图片,说法错误的是)

  • 微博怎么清空评论通知(微博怎么清空评论回复)

    微博怎么清空评论通知(微博怎么清空评论回复)

  • 电池健康度83%要换吗(电池健康度83%要换吗华为手机)

    电池健康度83%要换吗(电池健康度83%要换吗华为手机)

  • 快手极速版违反了规定怎么办(快手极速版违反社区规定怎么解封)

    快手极速版违反了规定怎么办(快手极速版违反社区规定怎么解封)

  • 主板csm要不要开启

    主板csm要不要开启

  • 黑鲨手机强制恢复出厂(黑鲨手机强制重启方法)

    黑鲨手机强制恢复出厂(黑鲨手机强制重启方法)

  • 苹果AE是哪个版本(苹果型号aea是什么版本)

    苹果AE是哪个版本(苹果型号aea是什么版本)

  • 手机qq咋关闭所在地(手机qq咋关闭所有的消息)

    手机qq咋关闭所在地(手机qq咋关闭所有的消息)

  • 抖音除了手机号还可以用什么注册(抖音除了手机号还有其他登录方式吗)

    抖音除了手机号还可以用什么注册(抖音除了手机号还有其他登录方式吗)

  • 微信朋友圈关闭后好友是不是就看不到(微信朋友圈关闭后别人能看到吗)

    微信朋友圈关闭后好友是不是就看不到(微信朋友圈关闭后别人能看到吗)

  • 手机屏幕一角发黑(手机屏幕一角发蓝紫色)

    手机屏幕一角发黑(手机屏幕一角发蓝紫色)

  • qq怎么伪装手机型号(qq伪装手机型号iphone软件)

    qq怎么伪装手机型号(qq伪装手机型号iphone软件)

  • a8 amd 9600相当于i几(a8 amd 9600相当于i5几代)

    a8 amd 9600相当于i几(a8 amd 9600相当于i5几代)

  • 平板微信下载解析包出错是什么原因(平板手机微信下载)

    平板微信下载解析包出错是什么原因(平板手机微信下载)

  • vivox30pro左边按键干嘛用(vivox30pro侧边按键)

    vivox30pro左边按键干嘛用(vivox30pro侧边按键)

  • ps怎么选中图片放大缩小(ps怎么选中图片中的文字)

    ps怎么选中图片放大缩小(ps怎么选中图片中的文字)

  • 计算机off键是什么键(计算机上off键的功能是什么)

    计算机off键是什么键(计算机上off键的功能是什么)

  • 风筝守护只能小米用吗(风筝守护下线)

    风筝守护只能小米用吗(风筝守护下线)

  • 快手为什么评论别人看不到(快手为什么评论不了表情包)

    快手为什么评论别人看不到(快手为什么评论不了表情包)

  • 三星s8上市了吗(三星s8上架时间)

    三星s8上市了吗(三星s8上架时间)

  • 哈罗单车如何取消自动续费(哈罗单车如何取消连续包月卡)

    哈罗单车如何取消自动续费(哈罗单车如何取消连续包月卡)

  • x9splus怎样设置防误触(x9手机怎么设置锁屏密码)

    x9splus怎样设置防误触(x9手机怎么设置锁屏密码)

  • avgcc.exe是什么进程 avgcc进程信息查询(avc格式是什么)

    avgcc.exe是什么进程 avgcc进程信息查询(avc格式是什么)

  • 个人名义开工程发票税率是多少
  • 公允价值变动借方是增加还是减少
  • 所得税费用期末应转入
  • 虚拟货币会计分录
  • 企业重组个人股东个人所得税
  • 新设备试运行时间
  • 工程款预付账款的账务处理
  • 电商平台退货退款流程图
  • 外汇银行会计特殊处理方法如何理解?
  • 农业生产企业是指什么
  • 无票收入有哪些情况抖音
  • 小额零星业务上限是多少
  • 一次发放数月的高温津贴如何计算个税?
  • 税金当月计提当月缴纳
  • 公司之间银行往来款怎么做账
  • 如何禁用Windows10更新
  • 银行对账单怎么修改
  • 如何计算每个月的天数
  • 禁用的网络在哪里开启
  • skynetave.exe - skynetave是什么进程 有什么用
  • 人际交往的作用是什么
  • 跨年的定额发票可以用吗
  • 员工出差火车票可以抵扣进项吗
  • 车船税怎么计账
  • 税收滞纳金可以抵税吗
  • 应收账款保理的主要意图在于
  • Honeybee flying over crocuses in the Tatra Mountains, Poland (© Mirek Kijewski/Getty Images)
  • apache去掉php后缀
  • 机动车空白发票作废税务局需要提供说明
  • 已提足折旧固定资产改建支出 所得税怎么计算
  • react roter
  • wordpress最新版本
  • Python Module — OpenAI ChatGPT API
  • 请求转发与重定义的区别
  • 什么是其他综合收益,包括哪些内容
  • 微信认证费是什么意思
  • 公司开具了电子发票
  • 人力资源外包服务企业
  • 差旅费取得发票怎么处理
  • 税务清单打印不全
  • mongodb运行
  • 辅助核算内容不完整
  • 增值税纳税申报类型怎么填
  • 固定业户应当向其机构
  • 电汇和票汇
  • mysql时间格式转换函数
  • 劳务合同如果不发工资怎么办
  • 含有增值税的工作有哪些
  • 购房契税如何入账
  • 固定资产台账具体做什么
  • 小微企业营业外收入二级科目怎么
  • 企业跨年度支出怎么计算
  • 银行利息为什么用红字
  • 交易性金融资产的入账价值
  • 案例分析以前年龄的变化
  • VirtualBox虚拟机中文免费
  • mssecsvc是什么进程
  • win7系统设置wifi热点
  • win8系统怎么设置开机启动项
  • ubuntu添加环境变量后黑屏
  • win7开机提示重启怎么办
  • linux的vi使用教程
  • cocos2dx怎么用
  • css英文代码翻译
  • cocos code ide 1.0.0 RC0 使用教程
  • Sublime Text + decoda 调试Quick-cocos2d-x 游戏
  • 跑酷角色左右移动怎么弄
  • js单线程多线程
  • node.js介绍
  • js中文本框设置的代码
  • 一个超简单的纸飞机
  • springmvc返回json格式的注解
  • 小米电脑安装ubuntu
  • 什么情况下税务局会罚款
  • 美国对中国纺织服装关税
  • 挂车买保险是怎么买的
  • 河南普通发票查询真伪查询系统
  • 江西省国家税务局李德平
  • 2021年江苏城镇医保网上缴费
  • 企业所得税预缴时间规定
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

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

    友情链接: 武汉网站建设