2011-05-10 48 views
2

insert insert方法在數據庫(PostgreSQL)的表中插入鏈接,但是如果發生錯誤,其餘的事務會受到影響,不起作用。例外是因爲該字段的URL是唯一的。繼續在Java中有例外的Postgres事務

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çãoatual FOI interrompida,comandos ignorados吃ØFIM做bloco德transação

谷歌的翻譯翻譯,我認爲是正確的:當前事務被中止,命令被忽略,直到交易塊結束爲止

回答

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) 
+0

我不知道這是否會導致很大的性能損失,但是在插入每個鏈接之前創建一個保存點,並在失敗的情況下執行roolbackto操作,但事務沒有中止。 – 2011-05-10 21:58:01

+0

@Renato,我很高興它爲你工作。感謝您的複選標記。 – 2011-05-11 02:13:00

2

我不知道解決方法,可能有一個,但我根本不知道它是什麼。一旦Postgres在交易中遇到問題,它會殺死整個事情,你唯一的希望就是重新啓動它。這意味着您需要確保您的代碼在批量完成之前能夠正確運行(特別是重複的主鍵和缺少的外鍵等)。

+0

是的,或者如果他只是想繼續前進並忽略之前的錯誤,那麼請嘗試回滾。 – 2011-05-10 20:16:10

+0

嗯,當然,但是無論如何,這無疑會解除他的所有工作,所以無論如何他都必須重新啓動。 – 2011-05-10 20:59:29

+1

如果您將保存點設置回滾到,則不必將其全部丟棄並重新開始。 – 2011-05-11 04:38:44

0

您是否嘗試過將try提交到try塊(也許是finnaly塊)之外? 也許它與返回false有關(你是否試圖看到它的代碼是不檢查這個布爾值在以後的時間,做某種回滾,如果它是假的?)

+0

在仔細查看你的代碼和異常之後,我認爲在插入之前需要啓動一個不同的事務,這樣它就會失敗,它只會正確地停止這個原子事務(但是會影響性能開銷開始並提交大量交易)。 – 2011-05-10 20:12:13

1

聲明:我知道幾乎沒有關於Java/JDBC的信息。

兩個「解決方案」:

  • 不組在一個事務中的所有刀片,分別執行它們
  • 看看SAVEPOINT小號
0

的問題不應該是怎麼樣處理例外情況,而是如何防止例外情況。 This link應該爲您提供如何優雅地忽略由於重複鍵而失敗的插入信息。

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不存在(因此額外嵌套NOT EXISTS業務),並且如果url存在,則使用內部SELECT生成一行;如果url存在,則不使用行;然後,我們使用內部SELECT的行數作爲計數器,應該在link中插入多少行。對索引列進行的EXISTS檢查也應該相當快。

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 * 

這將允許你插入批量行無一例外地檢查每一行,返回的條款給你回什麼插入,生成的序列的PK等,這是最快的解決方案(除了COPY)。