【问题标题】:JDBC Return generated key or existing keyJDBC 返回生成的密钥或现有的密钥
【发布时间】:2015-02-10 12:42:29
【问题描述】:

我有一个带有唯一索引的表来消除重复(简化示例)

CREATE TABLE `domain` (
      `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
      `subdomain` VARCHAR(255) NOT NULL,
      `domain` VARCHAR(63) NOT NULL,
      `zone` VARCHAR(63) NOT NULL,
      PRIMARY KEY (`id`),
      UNIQUE INDEX `UNIQUE` (`subdomain` ASC, `domain` ASC, `zone` ASC),
    ENGINE = InnoDB;

我插入了很多行,我需要返回主键(对于其他一对多插入)。

我的问题是,我插入了很多重复项,我也需要返回这些键。

这是我的解决方案,但没有更简单的解决方案吗?有了这个我不能使用批量插入,我希望这是最有效的。

PreparedStatement selectDomain = connection.prepareStatement("SELECT id FROM domain WHERE subdomain = ? AND domain = ? AND zone = ?");
PreparedStatement insertDomain = connection.prepareStatement("INSERT INTO domain(subdomain, domain, zone) VALUES (?,?,?)", Statement.RETURN_GENERATED_KEYS);

public int insertDomain(String subdomain, String domain, String zone) throws SQLException {
        int domainId = 0;
        selectDomain.setString(1, subdomain);
        selectDomain.setString(2, domain);
        selectDomain.setString(3, zone);
        ResultSet resultSet = selectDomain.executeQuery();
        if (resultSet.next()) {
            domainId = resultSet.getInt(1);
        } else {
            insertDomain.setString(1, subdomain);
            insertDomain.setString(2, subdomain);
            insertDomain.setString(3, subdomain);
            insertDomain.executeUpdate();
            resultSet = insertDomain.getGeneratedKeys();
            if (resultSet.next()) {
                domainId = resultSet.getInt(1);
            }
        }
        selectDomain.clearParameters();
        insertDomain.clearParameters();
}

【问题讨论】:

    标签: java mysql jdbc prepared-statement


    【解决方案1】:

    据我了解,使用批处理执行并不那么容易。您的方法是获取自动生成的密钥的最佳方式。 JDBC 驱动的限制很少,而且版本不同,getGeneratedKeys() 适用于单个条目。

    请查看以下链接,它可能对您有所帮助:-

    How to get generated keys from JDBC batch insert in Oracle?

    http://docs.oracle.com/database/121/JJDBC/jdbcvers.htm#JJDBC28099

    【讨论】:

      【解决方案2】:

      你可以修改你的 INSERT 是这样的:

      INSERT INTO domain (subdomain, domain, zone)
      SELECT $subdomain, $domain, $zone
      FROM domain 
      WHERE NOT EXISTS(
          SELECT subdomain, domain, zone
          FROM domain d
          WHERE d.subdomain= $subdomain and d.domain=$domain and d.zone=$zone
      )
      LIMIT 1
      

      $subdomain, $domain, $zone 是您想要添加的标签(正确引用或作为占位符),如果它不存在的话。如果标签已经存在,这种方法甚至不会触发 INSERT(以及随后的自动增量浪费)。您可能会想出比这更好的 SQL,但上面应该可以解决问题。

      如果您的表被正确索引,那么用于存在检查的额外 SELECT 将很快,并且无论如何数据库都必须执行该检查。

      【讨论】:

      • 感谢您的回答,但我不明白 :-) 它给了我“不安全的更新警告”并且它没有解决我的问题 - 我需要返回 id(无论条目是否已经存在与否)。正如我理解你的查询,它检查重复,然后插入。我可以使用 INSERT IGNORE 安全地执行此操作,因为我在(子域、域、区域)上使用 UNIQUE INDEX...但结果是相同的 - 它不会在重复时返回密钥。