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

时间:2011-05-10 19:34:14

标签: java exception postgresql transactions

下面的插入方法在数据库中的表(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;
    }
}

葡萄牙语中的错误消息:跨文化的跨国家,comandosignoradosatéofim do blocodetransação

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

7 个答案:

答案 0 :(得分:2)

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

答案 1 :(得分:2)

如果失败在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)

答案 2 :(得分:2)

您可以通过更改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是否已存在。

UPDATE :上述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黑客。这背后的想法是如果url不存在则使用内部SELECT生成一行(因此额外嵌套的NOT EXISTS业务),如果url存在则不使用行;然后,我们使用内部SELECT中的行数作为计数器,以便在link中插入多少行。对索引列进行EXISTS检查也应该非常快。

答案 3 :(得分:1)

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

两个“解决方案”:

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

答案 4 :(得分:1)

这样做:

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

答案 5 :(得分:0)

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

答案 6 :(得分:0)

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