IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 数据重复提交解决方案 -> 正文阅读

[Java知识库]数据重复提交解决方案

当用户在新增一条数据时,快速点击多次提交按钮,若没有做拦截,那么将会导致新增多条无意义的数据

本文参考其他文章编写:原文地址1原文地址2

解决方案

  1. 前端点击提交后将按钮禁用
document.getElementById("btn").disabled = true;

:无法避免恶意用户调用接口提交数据,比如直接请求接口,并不通过前端页面

2.在Java代码增加synchronized关键字使提交数据一条一条执行

public synchronized Result<?> save (Model model){ return null }

:使用起来比较简单但性能很低,在处理数据时会有排队等候的问题,并且在多实例部署时无法起到作用
3.使用Redis结合拦截器进行拦截
参考文章

  • 引入pom
<!--SpringBoot的AOP-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
  • 自定义注解
import java.lang.annotation.*;


/**
 * 放置用户短时间内对数据进行重复提交
 */
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AvoidReSubmit {

    /**
     * 失效时间,即可以第二次提交间隔时长
     * 默认3秒
     * 单位毫秒
     * @return
     */
    long expireTime() default 30 * 1000L; 
}

  • 编写拦截器

import com.hk.frame.util.AvoidReSubmit;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;


import javax.annotation.Resource;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.TimeUnit;

/**
接口数据重复提交拦截器
 **/
@Aspect
@Component
public class AvoidReSubmitAspect  {
    @Resource
    private RedisTemplate redisTemplate;

    /**
     * 定义匹配规则,以便于后续拦截直接拦截submit方法,不用重复表达式
     * 
     * 此处的参数为注解包名:com.hk.frame.util.AvoidReSubmit
     */
    @Pointcut(value = "@annotation(com.hk.frame.util.AvoidReSubmit)")
    public void submit() {
    }

    @Before("submit()&&@annotation(avoidReSubmit)")
    public void doBefore(JoinPoint joinPoint, AvoidReSubmit avoidReSubmit) {

        // 拼装参数
        StringBuffer sb = new StringBuffer();
        for(Object object : joinPoint.getArgs()){
            sb.append(object);
        }

        String key = md5(sb.toString());
        long expireTime = avoidReSubmit.expireTime();
        ValueOperations valueOperations = redisTemplate.opsForValue();
        Object object = valueOperations.get(key);
        if(null != object){
            throw new RuntimeException("您已经提交了请求,请不要重复提交哦!");
        }
        valueOperations.set(key, "1", expireTime, TimeUnit.MILLISECONDS);
    }

    @Around("submit()&&@annotation(avoidReSubmit)")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint, AvoidReSubmit avoidReSubmit) throws Throwable {
        System.out.println("环绕通知:");
        Object result = null;
        result = proceedingJoinPoint.proceed();
        return result;
    }

    @After("submit()")
    public void doAfter() {
        System.out.println("******拦截后的逻辑******");
    }

    private String md5(String str){
        if (str == null || str.length() == 0) {
            throw new IllegalArgumentException("String to encript cannot be null or zero length");
        }
        StringBuffer hexString = new StringBuffer();
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(str.getBytes());
            byte[] hash = md.digest();
            for (int i = 0; i < hash.length; i++) {
                if ((0xff & hash[i]) < 0x10) {
                    hexString.append("0" + Integer.toHexString((0xFF & hash[i])));
                } else {
                    hexString.append(Integer.toHexString(0xFF & hash[i]));
                }
            }
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return hexString.toString();
    }

}

  • 使用
    /**
     * 添加数据
     *
     * @param sg
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "/add", method = RequestMethod.POST, produces = "application/json;charset=UTF-8")
    @AvoidReSubmit(expireTime = 1000 * 3)//3秒内无法重复请求
    public void addShoppingGuide(@RequestBody Map<String,Object> param) {
        return coas.add(param);
    }

4.通过数据库唯一约束进行限制,再从代码中捕捉异常

  • 给某个指定字段添加唯一约束
ALTER TABLE 表名  ADD unique(列名);

适用于,比如user表中只希望userName一样时阻止新增

  • 设置联合唯一索引
ALTER TABLE <表名> ADD UNIQUE KEY(列名1,列名2);

适用于,比如user表中,希望当userName和age都一样时阻止新增

补充
mysql 查询唯一索引

show index from 表名;

mysql删除唯一索引

alter table 表名 drop index 索引名称;
  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-03-30 18:09:58  更:2022-03-30 18:12:36 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 6:16:39-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码