繁体   English   中英

使用Java中的异常继续Postgres事务

[英]Continue Postgres transaction with exceptions in Java

下面的插入方法在数据库中的表(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çãofualualfoi interrompida,comandosignoradosatéoffim do blocodecransção

谷歌翻译的翻译,我认为是正确的: 当前交易中止,命令被忽略直到交易结束

我不知道一个方法,可能有一个,但我根本不知道它是什么。 一旦Postgres在事务中遇到问题,它就会杀死整个事情,你唯一的希望就是重启它。 这意味着您需要确保您的代码在批量处理之前正确运行(特别是重复主键和丢失外键等)。

如果故障发生在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)

您可以通过更改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检查也应该非常快。

免责声明:我几乎不了解Java / JDBC。

两个“解决方案”:

  • 不要将所有插入分组到一个事务中,单独执行它们
  • 看看SAVEPOINT s

做这个 :

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)。

您是否尝试过在try块之外移动提交(可能是一个finnaly块)? 也许它与返回false有关(你试过看到它代码不是在以后检查这个布尔值并且如果它是假的那么做某种回滚吗?)

问题不应该是如何处理异常,而是如何防止异常。 此链接应为您提供有关如何正常忽略因重复键而失败的插入的信息。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM