【问题标题】:Continue Postgres transaction with exceptions in Java使用 Java 中的异常继续 Postgres 事务
【发布时间】:2011-05-10 19:34:14
【问题描述】:

下面的insert方法在数据库(PostgreSQL)的一个表上插入链接,但是如果发生错误,其余的事务都会受到影响并且不起作用。例外是因为字段 URL 是唯一的。

在 PostgreSQL 中是否有可能在出现异常的情况下继续事务?

for (int i = 0, n = page.getLinks().length; i < n; i++) {
     linkBD.insert(page.getLink(i), idPage);
}            

public boolean insert(Link link, int idPage) {
    try {
        String sql = "INSERT INTO link (id,idPage,url,linkText,"
        + "visited,broken) VALUES(nextval('idLinkSqc'),?,?,?,?,?)";

        PreparedStatement pstm = Conn.conn.prepareStatement(sql);

        pstm.setInt(1, idPage);
        pstm.setString(2, link.getUrl());
        pstm.setString(3, link.getLinkText());
        pstm.setBoolean(4, false);
        pstm.setBoolean(5, false);

        pstm.execute();            
        Conn.commit();
        pstm.close();            
        return true;
    } catch (Exception e) {
        System.out.println("Erro inserindo link no banco de dados: " + e.getMessage());
        System.out.println("Erro no link: "+link.getUrl());
        return false;
    }
}

葡萄牙语中的错误消息:transação atual foi interrompida, comandos ignorados até o fim do bloco de transação

我认为谷歌翻译的翻译是对的:当前事务被中止,命令被忽略,直到事务块结束

【问题讨论】:

    标签: java exception postgresql transactions


    【解决方案1】:

    如果故障发生在 SAVEPOINT 内,则可以继续。这是 psql 中的一个示例:

    # create temporary table foo (i int primary key);
    CREATE TABLE
    

    开始一个事务并插入一行:

    # begin;
    BEGIN
    # insert into foo values(1);
    INSERT 0 1
    

    启动一个保存点,将同一行插入两次。这将导致错误:

    # savepoint bar;
    SAVEPOINT
    # insert into foo values(2);
    INSERT 0 1
    # insert into foo values(2);
    ERROR:  duplicate key value violates unique constraint "foo_pkey"
    

    回滚到保存点,然后插入另一行。

    # rollback to savepoint bar;
    ROLLBACK
    # insert into foo values(3);
    INSERT 0 1
    

    提交并看看有什么:

    # commit;
    COMMIT
    # select * from foo;
     i 
    ---
     1
     3
    (2 rows)
    

    【讨论】:

    • 我不知道这是否会产生很大的性能损失,但是在插入每个链接之前创建一个保存点并在失败的情况下对其进行 roolback 以确保事务没有中止。
    • @Renato,很高兴它对你有用。感谢您的勾选。
    【解决方案2】:

    我不知道解决方法,可能有一个,但我就是不知道它是什么。一旦 Postgres 在事务中出现问题,它就会杀死整个事务,而您唯一的希望就是重新启动它。这意味着您需要确保您的代码在批处理之前能够正确运行(特别是诸如重复的主键和缺少外键等)。

    【讨论】:

    • 是的,或者如果他只想继续前进并忽略之前的错误,请尝试回滚。
    • 嗯,当然,但这无论如何都会取消他的所有工作,所以无论如何他都必须重新开始。
    • 如果你设置了一个保存点来回滚,它就不必全部扔掉重新开始。
    【解决方案3】:

    您可以通过更改您的 INSERT 来避免违反约束(从而避免异常和事务问题)

    INSERT INTO link(id, idPage, url, linkText, visited, broken)
    SELECT nextval('idLinkSqc'), ?, ?, ?, ?, ?
    FROM link
    WHERE NOT EXISTS (SELECT url FROM link WHERE url = ?)
    LIMIT 1
    

    然后是额外的占位符:

    pstm.setString(6, link.getUrl());
    

    您还可以将原始 INSERT 替换为存储过程,该存储过程将在尝试执行 INSERT 之前检查新 URL 是否已存在。

    更新:上述 SQL 的更好版本是:

    INSERT INTO link (id, idPage, url, linkText, visited, broken)
    SELECT nextval('idLinkSqc'), ?, ?, ?, ?, ?
    FROM (
        SELECT 1
        WHERE NOT EXISTS (SELECT 1 FROM link WHERE url = ?)
    ) AS postgres_needs_this_alias
    

    最终结果应该是一样的,但是这个版本不需要LIMIT 1 hack。这背后的想法是,如果url 不存在,则使用内部SELECT 生成一行(因此额外嵌套的NOT EXISTS 业务),如果url 存在,则不生成行;然后,我们使用内部 SELECT 中的行数作为应该在link 中插入多少行的计数器。对索引列的EXISTS 检查也应该很快。

    【讨论】:

      【解决方案4】:

      免责声明:我对 Java/JDBC 几乎一无所知。

      两个“解决方案”:

      • 不要将所有插入分组到一个事务中,分别执行
      • 看看SAVEPOINTs

      【讨论】:

        【解决方案5】:

        这样做:

        INSERT INTO table (column list)
        SELECT v.* FROM (VALUES (....), (....), (....)) v
             LEFT JOIN table t ON t.t_unique_column=v.column1 -- choose the matching column
             WHERE t.t_unique_column IS NULL
        RETURNING *
        

        这将允许您插入大量行而不会对每一行进行异常检查,并且 RETURNING 子句会返回插入的内容、序列生成的 PK 等。这是最快的解决方案(除了 COPY)。

        【讨论】:

          【解决方案6】:

          您是否尝试过将提交移到 try 块之外(可能移到 finnaly 块)? 也许它与返回 false 有关(您是否尝试过查看代码稍后不会检查此布尔值并在它为 false 时进行某种回滚?)

          【讨论】:

          • 在仔细查看您的代码和异常之后,我认为需要在插入之前启动一个不同的事务,这样它会失败,它只会正确停止这个原子事务(但是意识到与启动和提交大量事务相关的性能开销)。
          【解决方案7】:

          问题不应该是如何处理异常,而是如何防止异常。 This link 应该为您提供有关如何优雅地忽略由于重复键而失败的插入的信息。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-03-01
            • 1970-01-01
            • 2017-10-07
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2020-08-09
            相关资源
            最近更新 更多