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

  • 花粉俱乐部为什么总是获取用户信息失败(花粉俱乐部为什么会登录异常)

    花粉俱乐部为什么总是获取用户信息失败(花粉俱乐部为什么会登录异常)

  • 腾讯视频能手机号登录吗(腾讯视频能手机登录吗)

    腾讯视频能手机号登录吗(腾讯视频能手机登录吗)

  • 抖音图片评论怎么弄(抖音图片评论怎么删除我给别人的评论)

    抖音图片评论怎么弄(抖音图片评论怎么删除我给别人的评论)

  • 华为手机免费贴膜是哪里都可以吗(华为手机免费贴膜几次)

    华为手机免费贴膜是哪里都可以吗(华为手机免费贴膜几次)

  • 苹果手机密码键盘只显示一半(苹果手机密码键盘怎么按出来)

    苹果手机密码键盘只显示一半(苹果手机密码键盘怎么按出来)

  • sd wan到底是什么(sd-wan sdn)

    sd wan到底是什么(sd-wan sdn)

  • iphone11qq消息不提醒(iphone11qq消息没有提示音)

    iphone11qq消息不提醒(iphone11qq消息没有提示音)

  • 优酷可以几个设备同时登录(优酷可以几个设备登录)

    优酷可以几个设备同时登录(优酷可以几个设备登录)

  • 华为手机来信息振动怎么关闭(华为手机来信息了在顶部显示怎么弄)

    华为手机来信息振动怎么关闭(华为手机来信息了在顶部显示怎么弄)

  • 怎么连接电视网络(怎么连接电视网线)

    怎么连接电视网络(怎么连接电视网线)

  • 16g内存卡能录几天(16g的内存卡能够录多长时间)

    16g内存卡能录几天(16g的内存卡能够录多长时间)

  • 快手id号和快手号有什么区别(快手id号和快手账号)

    快手id号和快手号有什么区别(快手id号和快手账号)

  • 苹果11的闪光灯在哪里设置(苹果11的闪光灯在哪里开)

    苹果11的闪光灯在哪里设置(苹果11的闪光灯在哪里开)

  • 如何下载b站上的视频(如何下载b站上的视频素材)

    如何下载b站上的视频(如何下载b站上的视频素材)

  • 电脑行距怎么调整(电脑行距怎么调整word)

    电脑行距怎么调整(电脑行距怎么调整word)

  • 安卓手机用久了很卡怎么办(安卓手机用久了卡顿怎么办)

    安卓手机用久了很卡怎么办(安卓手机用久了卡顿怎么办)

  • 新买的苹果11会自带膜吗(新买的iphone 11)

    新买的苹果11会自带膜吗(新买的iphone 11)

  • 苹果5sa1533是什么版本(苹果5a1533是什么版本)

    苹果5sa1533是什么版本(苹果5a1533是什么版本)

  • 苹果7充电没有闪电标志(苹果充电没有声音怎么回事)

    苹果7充电没有闪电标志(苹果充电没有声音怎么回事)

  • 华为9x怎么分屏(华为9x怎么分屏使用)

    华为9x怎么分屏(华为9x怎么分屏使用)

  • 谷歌是哪个国家开发的(谷歌是哪个国家的公司总部在哪里)

    谷歌是哪个国家开发的(谷歌是哪个国家的公司总部在哪里)

  • ipad电池如何检测(苹果ipad电池检测在哪里)

    ipad电池如何检测(苹果ipad电池检测在哪里)

  • 小米9可以用5g吗(小米9手机能用5g网络吗)

    小米9可以用5g吗(小米9手机能用5g网络吗)

  • Python 计算机视觉(八)—— OpenCV 进行图像增强

    Python 计算机视觉(八)—— OpenCV 进行图像增强

  • 服务类一般纳税人无进项
  • 小规模纳税人公户的钱怎么转出来
  • 新的税收
  • 建设工程劳务分包的规定
  • 计提当月社保会计分录
  • 普通发票作废要本人去拿吗
  • 办公室提前退租未摊完的装修费如何处理
  • 合伙企业的合伙协议
  • 关联交易所得税规定
  • 银行询证函费用计入财务费用还是管理费用
  • 研发场地租赁计什么科目
  • 固定资产尚未投入怎么办
  • 固定资产本期折旧额与上期不一样
  • 收购发票税率是什么意思
  • 增值税优惠政策有哪些
  • 小规模季报营业税怎么算
  • 金蝶期末调汇怎么生成凭证
  • 本月已认证发票还能撤销吗
  • 银行对账单不平衡
  • 个人所得税人员添加后如何操作
  • 房地产企业卖出去的房子需要缴纳城镇土地使用税吗
  • 现金流量表与资产负债表不符
  • 跨境电商小规模与一般纳税人
  • 购买一台电脑会计分录
  • qdcsfs.exe - qdcsfs是什么进程 有何作用
  • php句法规则详解图
  • 应交税金的会计科目
  • 预测编码方式
  • laravel auth:api
  • php反射获取方法参数
  • chatcters
  • 大前端入门指南
  • 基于深度学习的图像超分辨率——综述
  • wallengine
  • php类型约束用法有哪些
  • php实现会话的步骤
  • 不按发票金额付款说明
  • 工会经费计提按应付职工薪酬借方还是贷方?
  • 帝国cms采集标签
  • dedecms怎么改图片
  • 发票已认证未抵扣,开红字发票信息表后,愿票需要退回吗
  • 新个税累计免税额
  • sql server 2005 win10
  • 合伙企业税务处理办法
  • 现金支票最少多少起提
  • 小规模附加税减免政策2023最新
  • 营改增是否降低了企业税负
  • 软件开发过程付什么费用
  • 建筑劳务清包工账务处理
  • 什么是内部报酬吗
  • 年报中纳税总额是本年实际缴纳的还是应纳税额
  • 待抵扣进项税额是什么情况下用的
  • 阿里云 mybase
  • 一个基于口令的用户身份认证基本过程是怎样的?
  • win7系统怎么把字体调大
  • 微软停止更新win7
  • windows server 2008 日志文件在哪
  • mac电脑如何下载app
  • 电脑系统win7怎么看
  • mac怎么移动鼠标
  • 手动防止Ping攻击方法(无需防火墙)
  • mac terminal在哪里
  • win7的系统升级win10
  • windows7睡眠唤醒快捷键
  • gta4支持win10
  • linux安装linux
  • bootstrap导航有哪些
  • Unity3d中EventTrigger的封装
  • js模拟实现new
  • cocos2dx4.0入门
  • python发送短信到手机
  • 福建省电子税务局官网登录入口
  • 山东省关于退林还耕的规定
  • 地税网上预约
  • 物业公司收取的物业费用如何缴税
  • 2020年海南个人所得税新规定
  • 成都交房需要交什么费用
  • 税务备案表付汇日期是否可以提前
  • 2022年印花税最新规定反映了什么
  • 二套房办房产证税费怎么算的
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

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

    友情链接: 武汉网站建设