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

  • 购销合同印花税最新政策2023
  • 增值税的纳税人是谁
  • 股份公司解散是什么?
  • 购税盘需要什么东西
  • 固定资产报废需要哪些人签字
  • 什么情况需要预缴税款
  • 公司员工提成怎么交个税
  • 管理部门社保入哪个科目
  • 小企业会计准则和一般企业会计准则的区别
  • 2019最新运输发票样本
  • 2018年度企业所得税税率表
  • 周转材料低值易耗品科目借方登记低值易耗品的减少
  • 政府扶贫支出
  • 公司注销公告怎么发布
  • 固定资产累计折旧完了怎么办
  • 大型设备间距应至少大于几米
  • 分公司可以合伙吗
  • 职工教育经费税前扣除标准2023年
  • 特许权使用费的收入确认时间
  • 汇总缴纳增值税如何申报
  • 不动产有法律效力吗
  • 关于两套账老板和会计须知的4个点
  • 改建或新建建筑物含义
  • 员工出差报销制度
  • 购买使用过的固定资产如何计提折旧
  • 工程项目管理人员任命书
  • 去办税大厅清卡需要带什么
  • 软件技术开发是什么
  • 库存商品进货价怎么算
  • 仓库的账怎么做
  • 如何看云空间的内容
  • 结转人工费会计分录
  • 接的网线影响传输吗
  • macos使用方法
  • 小规模纳税人冲红发票怎么报税
  • 企业备用金预借多久到账
  • 留底的进项税额记哪儿?
  • 增值税三流合一涉及子公司
  • 一品红叶发黄咋回事儿
  • 水电费分割单会计分录
  • 查补的增值税账务处理
  • 小微企业认定标准时间
  • 导入vue.js
  • 资产证券化会计处理案例
  • 承租方承担税费
  • 二季度盈利三季度亏损
  • 现金流量表四个部分
  • 事业单位相关规定有哪些
  • 公户的结算卡是否可以转账
  • 委托加工物资的成本包括
  • 股东权益合计等于净资产吗
  • 材料暂估入库的账务处理
  • 应付票据转应付账款有附件吗
  • 单位组织活动主持词
  • 记账凭证模板
  • 专栏设计模板
  • mysql varchar2
  • sql server real
  • mysql字段什么意思
  • sqlserver存储过程返回多个结果集
  • 因windowssystem32
  • windows7用户
  • winpe安装系统教程
  • win edge
  • 安装ubuntu 20.10
  • win7鼠标右键一闪就没了
  • win7怎么免费升级win11
  • win7屏幕颜色不正常
  • smss.exe什么意思
  • windows允许访问
  • cocos2dx解密
  • nodejs web3
  • python 爬虫例子
  • about ContentProvider
  • 行为怪异的人有问题吗
  • Android开发工具
  • jQuery插件封装时如要实现链式编程,需要
  • 嵌入式安卓开发入门
  • 苏州买房退契税政策2023
  • 车辆保险代收车船税会计分录
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

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

    友情链接: 武汉网站建设