【问题标题】:java and postgresql: duplicate key value violation, despite sending none duplicate keysjava和postgresql:重复键值违规,尽管没有发送重复键
【发布时间】:2016-08-02 12:23:37
【问题描述】:

从我的推断来看,我似乎试图在我的 postgresql 数据库中插入一个新行,并使用一个新的主键组合。然而它抱怨我多次插入相同的键。

证据(来自错误日志):

ERROR com.saxo.read.postgres.RequestLoggingStore - Unable to insert request correctly, uri: /someUrlHere, uuid: a7813f97-7c0d-4739-b20b-a2a6ab2f32a3, at: 1470137399294243
org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "requestlogging_pkey"
  Detail: Key (id, "timestamp")=(a7813f97-7c0d-4739-b20b-a2a6ab2f32a3, 1470137399294243) already exists.
        at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2270)
        at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1998)
        at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:255)
        ....
ERROR com.saxo.read.postgres.RequestLoggingStore - Unable to insert request correctly, uri: /someUrlHere, uuid: 1891dc28-64b8-4a16-8d22-67af6a605aa0, at: 1470137399293662
org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "requestlogging_pkey"
  Detail: Key (id, "timestamp")=(a7813f97-7c0d-4739-b20b-a2a6ab2f32a3, 1470137399294243) already exists.
        at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2270)
        at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1998)
        at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:255)
        ....

如您所见,PQSL 抱怨我有重复的密钥违规,它应该认为,如果我给了它抱怨的值。 (a7813f97-7c0d-4739-b20b-a2a6ab2f32a3, 1470137399294243) 是重复键。但是,从我在 RequestLoggingStore 中的错误日志可以看出,报告的 uuid 和时间戳与 PSQL 报告的不同,并且不是重复键。

我立即假设我在代码中犯了错误,并且报告的 UUID 和时间戳不是我插入到 postgresql 数据库中的内容。但我从下面的代码看不出这是怎么可能的:

public String put(String uri, String method, String body, String headers, String userId) {
        String uuid = null;
        long timestamp = stamper.timestamp();
        try {
            uuid = java.util.UUID.randomUUID().toString();

            insertQuery.setString(1, uri);
            insertQuery.setString(2, body);
            insertQuery.setString(3, method);
            insertQuery.setString(4, headers);
            insertQuery.setString(5, userId);
            insertQuery.setLong(6, timestamp);
            insertQuery.setString(7, uuid);

            insertQuery.execute();
            return uuid;
        } catch (Exception e) {
            logger.error("Unable to insert request correctly, uri: {}, uuid: {}, at: {}", uri, uuid, timestamp, e);
            return null;
        }
    }

以防万一您还想查看插入查询:

insertQuery = postgresConnection.prepareStatement("INSERT INTO "+TableName+"( "+listOfArgs+" ) VALUES ( ?, ?, ?, ?, ?, ?, ? );");

UUID 应该为空,或者与我的错误记录器报告的值相同,对吗?

两者之间怎么会有区别?

是我弄错了还是 psql 的 java 驱动程序有问题?

我正在将所有传入请求记录到我的 Web 服务/API。测试时它似乎工作正常。在高负载下是否会变得压力过大?

【问题讨论】:

  • 也显示表架构
  • 你能不能有一些触发器在插入时做另一个插入?
  • 如果没有 SSCCE,很难回答。我已经使用 PostgreSQL 的 JDBC 连接器好几年了,从来没有发现任何如此严重的问题。您使用的是多线程还是事务?
  • 附带说明,不要从try 或catch 块内返回。
  • 顺便说一句,insertQuery 是在哪里定义的?它在哪里关闭?

标签: java postgresql constraints primary-key sql-insert


【解决方案1】:

由于insertQuery 是一个类字段,所以put 方法可以在两个线程中调用并覆盖相同的唯一SQL 字段。因此,在执行“相同”记录 (insertQuery) 时会尝试。

最好的办法是全部保存在局部变量中,并尝试使用资源。

(也可以将密钥生成留给数据库,查询PreparedStatement.getGeneratedKeys()。)

【讨论】:

  • 这似乎很合理。因为我确实有几个线程在运行,都接受传入的请求。如果我理解正确,解决方案是用 try (PreparedStatement insertQuery = postgresConnection.prepareStatement("INSERT INTO "+TableName+"( "+listOfArgs+" ) VALUES ( ?, ?, ?, ?, ?, ? , ? );"); ){
  • 是的,这似乎给出了一个可重入的 put 方法
  • 顺便说一下 PreparedStatement.executeUpdate 会更好,而 clearParameters 在其他情况下可能会派上用场。 致所有来到这里的读者。
  • 为每个 put 方法制作一个新的准备好的语句确实解决了这个问题。 Jopp,欢迎您在上面发表评论。我应该使用更新而不是插入?
  • 我的意思是我看到PreparedStatement.execute() (?) 其中PreparedStatement.executeUpdate() 更合适——这也适用于INSERT/DELETE 和此类SQL 语句。
猜你喜欢
  • 2021-09-03
  • 2016-08-02
  • 1970-01-01
  • 1970-01-01
  • 2016-09-28
  • 2017-12-18
  • 2011-05-25
  • 1970-01-01
相关资源
最近更新 更多