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

  • 增加微博粉丝的18个方法途径(增加微博粉丝的技巧有哪些)

    增加微博粉丝的18个方法途径(增加微博粉丝的技巧有哪些)

  • 企业微信与普通微信的区别(企业微信与普通微信聊天能看到信息已读吗)

    企业微信与普通微信的区别(企业微信与普通微信聊天能看到信息已读吗)

  • 华为h60-l01是什么型号(华为型号h60_l01是什么样手机?)

    华为h60-l01是什么型号(华为型号h60_l01是什么样手机?)

  • 笔记本电脑键盘没反应怎么办(笔记本电脑键盘更换要多少钱?)

    笔记本电脑键盘没反应怎么办(笔记本电脑键盘更换要多少钱?)

  • 淘宝好友能看到我的足迹吗(淘宝好友能看到闲鱼发布的东西吗)

    淘宝好友能看到我的足迹吗(淘宝好友能看到闲鱼发布的东西吗)

  • 微信专清里的文件可以清理吗(微信专清里的文件没用过怎么会有)

    微信专清里的文件可以清理吗(微信专清里的文件没用过怎么会有)

  • 魅族手机收不到短信验证码怎么办(魅族手机收不到验证码怎么解决)

    魅族手机收不到短信验证码怎么办(魅族手机收不到验证码怎么解决)

  • 小米电脑截屏怎么截(小米电脑截屏怎么操作)

    小米电脑截屏怎么截(小米电脑截屏怎么操作)

  • iphone11处理器(iphone11处理器是a几)

    iphone11处理器(iphone11处理器是a几)

  • qq头像怎么变灰色(qq头像怎么变灰哀悼)

    qq头像怎么变灰色(qq头像怎么变灰哀悼)

  • 错误代码10000什么意思(错误代码100009)

    错误代码10000什么意思(错误代码100009)

  • 华为手机怎么调闹钟一次(华为手机怎么调回彩色屏)

    华为手机怎么调闹钟一次(华为手机怎么调回彩色屏)

  • kingroot有风险吗(kingroot怎么下架了)

    kingroot有风险吗(kingroot怎么下架了)

  • 离线请留言是屏蔽了吗(离线请留言是退出登录了吗)

    离线请留言是屏蔽了吗(离线请留言是退出登录了吗)

  • 删除了微信好友再添加对方能看到吗(删除了微信好友还能加回来吗)

    删除了微信好友再添加对方能看到吗(删除了微信好友还能加回来吗)

  • 浏览器下载的视频在哪个文件夹(浏览器下载的视频怎么发到微信)

    浏览器下载的视频在哪个文件夹(浏览器下载的视频怎么发到微信)

  • 为什么七天网络未授权(为什么七天网络查不到成绩)

    为什么七天网络未授权(为什么七天网络查不到成绩)

  • 默认拨号怎么取消(默认拨号设置)

    默认拨号怎么取消(默认拨号设置)

  • 滴滴成交率在哪里看(滴滴成交率在哪里查看)

    滴滴成交率在哪里看(滴滴成交率在哪里查看)

  • bt怎么转化成视频

    bt怎么转化成视频

  • 酷狗vip可以2人一起用吗(酷狗vip可以2人应该怎么登陆)

    酷狗vip可以2人一起用吗(酷狗vip可以2人应该怎么登陆)

  • 入门级单反和专业级单反的区别(入门级单反值得买吗)

    入门级单反和专业级单反的区别(入门级单反值得买吗)

  • 为Llinux系统添加交换分区(swap space)的方法(linux添加系统用户命令)

    为Llinux系统添加交换分区(swap space)的方法(linux添加系统用户命令)

  • 如何用css选择器选中某个类的第一个元素(css选择器 菜鸟教程)

    如何用css选择器选中某个类的第一个元素(css选择器 菜鸟教程)

  • 【WebSocket 协议】Web 通信的下一步进化(websocket tcpsocket)

    【WebSocket 协议】Web 通信的下一步进化(websocket tcpsocket)

  • testdisk命令  修复磁盘文件(testdisk安装方法)

    testdisk命令 修复磁盘文件(testdisk安装方法)

  • 劳务派遣公司如何收费
  • 小规模纳税人单张发票最大金额
  • 差额征税如何开票备注内容应该怎么调
  • 分税制财政管理体制是什么?
  • 多缴纳了印花税有什么影响
  • 劳务费个人所得税怎样计算
  • 交强险怎么报销流程
  • 公司报销房租还能扣税吗
  • 一般纳税人收入会计分录
  • 通行费发票怎么合并一张发票
  • 收到抵物广告费的会计处理怎么做?
  • 车辆买的商业险有家庭包
  • 预付绩效工资是什么意思
  • 哪些税费计入应缴税费
  • 金税盘的年费怎么做抵扣帐
  • 集团企业涉税需求网络
  • profiler.exe - profiler是什么进程 有什么用
  • 如何安装u盘的系统
  • 稽查补交的税款怎么处理
  • windows7怎样升级windows10
  • php preg_grep
  • php 实例
  • wordpress 中文版和国际版区别
  • 海外供应商贸易平台
  • php实现日历
  • 最贵的节能灯泡品牌
  • vue引入mui
  • php扩展开发参考手册
  • 兼职老师工资怎么扣税
  • uniapp实战
  • 最好的ph计
  • thinkphp+vue
  • thinkphp怎么用
  • gps定位器怎么开启
  • php str
  • 建行对账单回签平啥意思
  • 命令行修改mbr
  • 会计怎么计算
  • 购买方已认证怎么开具红字信息表
  • 安装织梦数据库连接不上
  • mysql常见操作
  • 租集体土地建厂合法吗
  • 一般纳税企业增值税的核算应当使用
  • mysql的左右连接
  • 银行电子承兑到期了怎么兑现操作
  • 公司模具部制作工作内容
  • 会计凭证借贷方哪个是收入
  • 会计学材料成本差异
  • 宾馆购买的床财务怎么做账
  • 出口退回的增值税计入哪个会计科目
  • 待处理财产损益的账务处理
  • 税控盘冲红怎么操作
  • 仲裁期间公司发工资了怎么办
  • sql like多个
  • mysql存储数据的原理
  • windows server 2008 64位MySQL5.6免安装版本配置方法图解
  • 提高搜索
  • windows2008 iis7如何取消目录执行权限的详细图文介绍
  • windows无法预览文件
  • 3ds怎么用3ds格式
  • nodejs child_process
  • linux中的shell命令
  • python外星人入侵游戏
  • 中文路径和英文路径
  • vue插件使用
  • ubuntu20.04怎么安装
  • 批处理系统的进程调度算法
  • jquery遍历object
  • jquery对动态生成的进行操作
  • 安卓开发主要做什么
  • javascrip
  • python asyncio await
  • 辽宁省农村合作医疗2024怎么缴费
  • 12366纳税服务热线存在的问题
  • 税控开票软件里的汇总怎么弄
  • 柳州 税务
  • 开票信息电子版怎么做
  • 沈阳和平税务大厅电话
  • 古代税收和现代税收
  • 浙江摩托车上牌条件
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

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

    友情链接: 武汉网站建设