【问题标题】:Java PreparedStatement and ON DUPLICATE KEY UPDATE: how do I know whether row was inserted or updated?Java PreparedStatement 和 ON DUPLICATE KEY UPDATE:我如何知道行是插入还是更新?
【发布时间】:2014-05-09 17:24:03
【问题描述】:

有了以下代码,我怎么知道 execute() 方法是插入还是更新?:

Connection c = DriverManager.getConnection(connectionString);

PreparedStatement st = c.prepareStatement("INSERT INTO `table`(`field1`) VALUES (?) ON DUPLICATE KEY UPDATE id=LAST_INSERT_ID(id);");

st.setString(1,"some value");
st.execute();

提前致谢。

【问题讨论】:

    标签: java mysql sql database jdbc


    【解决方案1】:

    考虑以下 MySQL 测试表:

    CREATE TABLE `customers` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `name` varchar(100) NOT NULL,
      `email` varchar(100) NOT NULL,
      PRIMARY KEY (`id`),
      UNIQUE KEY `email` (`email`)
    ) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
    

    现有样本数据如下:

    id  name            email
    --  --------------  ----------------
     1  Loblaw, Bob     bob@example.com
     2  Thompson, Gord  gord@example.com
    

    使用默认连接设置compensateOnDuplicateKeyUpdateCounts=false(描述here)以下Java代码

    PreparedStatement ps = dbConnection.prepareStatement(
            "INSERT INTO customers (name, email) " +
            "VALUES (?, ?) " +
            "ON DUPLICATE KEY UPDATE " +
                "name = VALUES(name), " +
                "id = LAST_INSERT_ID(id)");
    ps.setString(1, "McMack, Mike");
    ps.setString(2, "mike@example.com");
    int euReturnValue = ps.executeUpdate();
    System.out.printf("executeUpdate returned %d%n", euReturnValue);
    Statement s = dbConnection.createStatement();
    ResultSet rs = s.executeQuery("SELECT LAST_INSERT_ID() AS n");
    rs.next();
    int affectedId = rs.getInt(1);
    if (euReturnValue == 1) {
        System.out.printf("    => A new row was inserted: id=%d%n", affectedId);
    }
    else {
        System.out.printf("    => An existing row was updated: id=%d%n", affectedId);
    }
    

    产生以下控制台输出

    executeUpdate returned 1
        => A new row was inserted: id=3
    

    现在使用参数值再次运行相同的代码

    ps.setString(1, "Loblaw, Robert");
    ps.setString(2, "bob@example.com");
    

    控制台输出是

    executeUpdate returned 2
        => An existing row was updated: id=1
    

    这表明如果唯一索引导致现有行被更新,.executeUpdate 确实可以返回 2。如果您在实际测试代码方面需要进一步帮助,那么您应该编辑您的问题以包含它。

    编辑

    进一步测试表明.executeUpdate 将返回 1 如果

    1. 尝试的 INSERT 被中止,因为它会导致重复的 UNIQUE 键值,
    2. 指定的 ON DUPLICATE KEY UPDATE 更改实际上并未修改现有行中的任何值

    这可以通过使用完全相同的参数值连续两次运行上述测试代码来确认。请注意,UPDATE ... id = LAST_INSERT_ID(id)“技巧”确实确保返回正确的 id 值。

    如果插入的唯一值是唯一键值,这可能解释了 OP 的测试结果。

    【讨论】:

    • 哇,感谢您的详细解释!那么,在查询中添加一些像“name = VALUES(name)”这样的赋值会使 executeUpdate() 在更新时返回 2?我会尽力做到这一点。您能否详细说明为什么 SELECT_LAST_INSERT_ID() 不能确保返回正确的 id 值?再次感谢。
    • @ed22 (1) 回复:“那么,在查询中添加一些赋值,如 'name = VALUES(name)' 会使 executeUpdate() 在更新时返回 2?” - 不必要。为了使executeUpdate 在更新时始终返回 2,您需要确保 UPDATE 实际上 更改 某些内容。一个明显的候选者是lastUpdated 列,用于保存最近更改(插入或更新)的日期/时间。 (2) 正如我所说,即使executeUpdate 在更新时返回 1,SELECT_LAST_INSERT_ID() 函数确实 也能工作,所以这不是真正的问题。
    • @GordThompson 你确定这是正确的吗?文件说 executeUpdate 将返回已更改的行数,因此当插入新行或使用更改更新时返回 1,如果它是重复的并且没有任何更改,则返回 0,或者如果插入 7 行则返回 7例如,或 4 个插入和 3 个修改。
    • @Chiquis 我确信我的回答准确地描述了我执行的实际测试的结果。您指的是哪些“文档”:executeUpdate 的 JDBC 文档,还是 MySQL Connector/J 的特定文档?
    • @GordThompson 我指的是这个docs.oracle.com/javase/7/docs/api/java/sql/…,上面写着“(1)SQL 数据操作语言(DML)语句的行数或(2)0 表示什么都不返回的SQL 语句”
    【解决方案2】:

    改用executeUpdate,因为它返回int 行数。

    更新1:根据MySQL INSERT ... ON DUPLICATE KEY UPDATE documentation

    如果使用 ON DUPLICATE KEY UPDATE,每行受影响的行值为 1,如果 该行作为新行插入,如果更新现有行,则插入 2。

    更新 2INSERT IGNORE 也可能是 option

    INSERT IGNORE INTO `table`(`field1`) VALUES (?)
    

    executeUpdate 应该在插入新行时返回 1,在有重复行时返回 0。

    【讨论】:

    • 感谢您的回答,但它不适合我。无论执行插入还是更新,它都返回 1。我需要知道的是执行的操作是插入还是更新(我需要知道是哪一个)。
    • 我相信 MySQL 会使用返回码来通知你是否发生了插入(返回 1)或更新(返回 2):dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html
    • 我不确定为什么无论插入还是更新,返回的受影响的行数都是相同的,我目前没有 MySQL 服务器可以测试。跨度>
    • @ed22 - 这个答案是正确的。对于INSERT ... ON DUPLICATE KEY 查询,.executeUpdate() 如果插入新行则返回 1,如果更新现有行则返回 2。您问题中的示例代码可能会产生误导,因为如果该行已经存在,那么实际上没有什么可以改变的,所以.executeUpdate() 可能会返回一个意外的值。如果您可以创建一个示例查询来更好地说明您的问题,那么请编辑您的问题以包含它。
    • 实际上,我相信LAST_INSERT_ID()“技巧”只是确保LAST_INSERT_ID()返回受影响记录的id值的一种方式,即使它是现有行(并且实际上没有执行 INSERT)。有关详细信息,请参阅我的答案中的示例。
    猜你喜欢
    • 1970-01-01
    • 2016-08-28
    • 1970-01-01
    • 2015-03-26
    • 1970-01-01
    • 2018-05-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-13
    相关资源
    最近更新 更多