方案 1:使用 MySQL 的 INSERT ... ON DUPLICATE KEY UPDATE 語句(推薦)

MySQL 原生支持 INSERT ... ON DUPLICATE KEY UPDATE 語法,當插入的記錄違反 唯一約束(如主鍵或唯一索引)時,會執行更新操作;否則執行插入操作。

前提條件

需要為表設置 唯一約束(主鍵 PRIMARY KEY 或唯一索引 UNIQUE),確保 “判斷記錄是否存在” 的依據(如某個字段或組合字段唯一)。

示例步驟

  1. 創建測試表(含唯一約束):





    sql









CREATE TABLE user (
    id INT PRIMARY KEY,  -- 主鍵(唯一約束)
    name VARCHAR(50),
    age INT
);
  1. Java 代碼實現





    java



    運行






import java.sql.*;

public class MysqlUpsert {
    // 數據庫連接信息
    private static final String URL = "jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC";
    private static final String USER = "root";
    private static final String PASSWORD = "123456";

    public static void upsertUser(int id, String name, int age) {
        // SQL 語句:若 id 已存在則更新 name 和 age,否則插入新記錄
        String sql = "INSERT INTO user (id, name, age) " +
                     "VALUES (?, ?, ?) " +
                     "ON DUPLICATE KEY UPDATE " +  // 觸發條件:違反唯一約束(此處為 id 重複)
                     "name = ?, age = ?";         // 更新的字段(注意參數順序)

        try (Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
             PreparedStatement pstmt = conn.prepareStatement(sql)) {

            // 設置插入的參數(id, name, age)
            pstmt.setInt(1, id);
            pstmt.setString(2, name);
            pstmt.setInt(3, age);

            // 設置更新的參數(name, age)—— 與上面的 name 和 age 一致
            pstmt.setString(4, name);
            pstmt.setInt(5, age);

            // 執行 SQL(返回受影響的行數:插入成功返回1,更新成功返回2)
            int rowsAffected = pstmt.executeUpdate();
            System.out.println(rowsAffected > 0 ? "操作成功" : "操作失敗");

        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        // 測試:第一次執行是插入(id=1不存在),第二次執行是更新(id=1已存在)
        upsertUser(1, "張三", 20);  // 插入
        upsertUser(1, "張三", 21);  // 更新(年齡從20→21)
    }
}

方案 2:先查詢再判斷(適用於不支持 ON DUPLICATE KEY 的場景)

如果因數據庫版本或其他限制無法使用方案 1,可通過 “先查詢記錄是否存在,再決定執行插入或更新” 實現:

java

運行

public static void upsertUserByCheck(int id, String name, int age) {
    Connection conn = null;
    PreparedStatement pstmt = null;
    ResultSet rs = null;

    try {
        conn = DriverManager.getConnection(URL, USER, PASSWORD);
        conn.setAutoCommit(false); // 開啓事務

        // 1. 查詢記錄是否存在
        String checkSql = "SELECT id FROM user WHERE id = ?";
        pstmt = conn.prepareStatement(checkSql);
        pstmt.setInt(1, id);
        rs = pstmt.executeQuery();

        if (rs.next()) {
            // 2. 記錄存在:執行更新
            String updateSql = "UPDATE user SET name = ?, age = ? WHERE id = ?";
            pstmt = conn.prepareStatement(updateSql);
            pstmt.setString(1, name);
            pstmt.setInt(2, age);
            pstmt.setInt(3, id);
        } else {
            // 3. 記錄不存在:執行插入
            String insertSql = "INSERT INTO user (id, name, age) VALUES (?, ?, ?)";
            pstmt = conn.prepareStatement(insertSql);
            pstmt.setInt(1, id);
            pstmt.setString(2, name);
            pstmt.setInt(3, age);
        }

        pstmt.executeUpdate();
        conn.commit(); // 提交事務
        System.out.println("操作成功");

    } catch (SQLException e) {
        try {
            if (conn != null) conn.rollback(); // 事務回滾
        } catch (SQLException ex) {
            ex.printStackTrace();
        }
        e.printStackTrace();
    } finally {
        // 關閉資源(rs, pstmt, conn)
        try { if (rs != null) rs.close(); } catch (SQLException e) {e.printStackTrace();}
        try { if (pstmt != null) pstmt.close(); } catch (SQLException e) {e.printStackTrace();}
        try { if (conn != null) conn.close(); } catch (SQLException e) {e.printStackTrace();}
    }
}

兩種方案對比

方案

優點

缺點

適用場景

INSERT ... ON DUPLICATE KEY

性能好(一次 SQL 操作)、原子性強

依賴唯一約束,僅 MySQL 支持

支持該語法的 MySQL 環境

先查詢再操作

兼容性好(所有數據庫通用)

性能較差(至少兩次 SQL 操作)、需手動控制事務

不支持 ON DUPLICATE KEY 的場景

注意事項

  1. 唯一約束:方案 1 必須確保表有唯一約束(主鍵或唯一索引),否則 ON DUPLICATE KEY UPDATE 不會生效。
  2. 參數順序INSERT ... ON DUPLICATE KEY UPDATE 中,更新的參數需要在插入參數之後重新設置(如示例中位置 4 和 5)。
  3. 事務:方案 2 需手動開啓事務,避免併發場景下的重複插入問題。