You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用JavaScript Fetch+MySQL后端时,如何避免SQL重复插入?

解决双击提交导致重复数据插入的方案

前端层面(即时阻止重复请求)

1. 提交时禁用按钮

点击提交后立刻禁用按钮,直到请求完成(成功/失败)再恢复,从交互层面直接阻止重复点击:

const submitBtn = document.getElementById('submit-btn');
const form = document.getElementById('data-form');

form.addEventListener('submit', async (e) => {
  e.preventDefault();
  // 禁用按钮并修改提示文本
  submitBtn.disabled = true;
  submitBtn.textContent = '提交中...';

  const formData = new FormData(form);
  try {
    await fetch('/api/submit', {
      method: 'POST',
      body: formData
    });
    alert('提交成功');
  } catch (err) {
    console.error('提交失败:', err);
    alert('提交失败,请重试');
  } finally {
    // 请求结束后恢复按钮状态
    submitBtn.disabled = false;
    submitBtn.textContent = '提交';
  }
});

2. 使用状态锁防止重复触发

用变量标记当前是否处于提交状态,避免同一时间发起多个请求:

let isSubmitting = false;
const form = document.getElementById('data-form');

form.addEventListener('submit', async (e) => {
  e.preventDefault();
  if (isSubmitting) return; // 正在提交则直接终止

  isSubmitting = true;
  const formData = new FormData(form);

  try {
    await fetch('/api/submit', {
      method: 'POST',
      body: formData
    });
    alert('提交成功');
  } catch (err) {
    console.error(err);
    alert('提交失败');
  } finally {
    isSubmitting = false;
  }
});

后端层面(兜底防重复插入)

前端限制可能被绕过(比如手动修改DOM、直接调用接口),必须在后端做兜底校验:

1. 数据库添加唯一索引

根据业务逻辑,给表添加唯一约束,比如针对用户ID+提交内容的组合创建唯一索引:

-- 示例:给表单提交表添加唯一索引,防止同一用户重复提交相同内容
ALTER TABLE form_submissions 
ADD UNIQUE KEY idx_user_content (user_id, MD5(content));

后端插入时捕获唯一索引冲突错误,直接返回提示:

// Express 接口示例
app.post('/api/submit', async (req, res) => {
  const { userId, content } = req.body;
  try {
    await db.query(
      'INSERT INTO form_submissions (user_id, content) VALUES (?, ?)',
      [userId, content]
    );
    res.status(200).json({ success: true, msg: '提交成功' });
  } catch (err) {
    // 捕获MySQL唯一索引冲突错误(错误码1062)
    if (err.code === 'ER_DUP_ENTRY') {
      return res.status(400).json({ success: false, msg: '请勿重复提交' });
    }
    res.status(500).json({ success: false, msg: '服务器错误' });
  }
});

2. 接口幂等性设计

前端提交时生成唯一requestId(比如UUID),后端先检查该ID是否已处理,未处理则执行插入并记录ID:

// 前端生成requestId
const requestId = crypto.randomUUID(); // 浏览器支持crypto API,或用第三方库生成

// 后端接口处理
app.post('/api/submit', async (req, res) => {
  const { requestId, userId, content } = req.body;
  
  // 检查requestId是否已存在(可存在Redis或专门的幂等记录表)
  const exists = await db.query('SELECT id FROM request_records WHERE request_id = ?', [requestId]);
  if (exists.length > 0) {
    return res.status(200).json({ success: true, msg: '已提交' });
  }

  try {
    // 开启事务,同时插入提交记录和幂等记录
    await db.beginTransaction();
    await db.query('INSERT INTO form_submissions (user_id, content) VALUES (?, ?)', [userId, content]);
    await db.query('INSERT INTO request_records (request_id) VALUES (?)', [requestId]);
    await db.commit();
    res.status(200).json({ success: true, msg: '提交成功' });
  } catch (err) {
    await db.rollback();
    if (err.code === 'ER_DUP_ENTRY') {
      return res.status(400).json({ success: false, msg: '请勿重复提交' });
    }
    res.status(500).json({ success: false, msg: '服务器错误' });
  }
});

3. 事务加排他锁

通过数据库事务加排他锁,确保同一时间只有一个请求能处理相同提交:

app.post('/api/submit', async (req, res) => {
  const { userId, content } = req.body;
  try {
    await db.beginTransaction();
    // 查询时加排他锁(FOR UPDATE),锁定当前查询结果
    const existing = await db.query(
      'SELECT id FROM form_submissions WHERE user_id = ? AND content = ? FOR UPDATE',
      [userId, content]
    );
    if (existing.length > 0) {
      await db.rollback();
      return res.status(400).json({ success: false, msg: '请勿重复提交' });
    }
    // 插入新记录
    await db.query('INSERT INTO form_submissions (user_id, content) VALUES (?, ?)', [userId, content]);
    await db.commit();
    res.status(200).json({ success: true, msg: '提交成功' });
  } catch (err) {
    await db.rollback();
    res.status(500).json({ success: false, msg: '服务器错误' });
  }
});

内容的提问来源于stack exchange,提问作者Adeniyi Abraham

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 09:22:06