簡體   English   中英

將多個查詢作為一個 dblink 事務執行

[英]Execute multiple queries as one dblink transaction

我在 Java 應用程序中工作,我需要同時執行這兩個查詢(作為 Java 中的字符串),並在出現錯誤時回滾事務。

SELECT dblink_exec('hostaddr=xxx.xx.xxx.xxx port=5432 dbname=bdname user=myuser password=mypass connect_timeout=2',
'INSERT INTO table3(field4) 
VALUES (5)') AS result;

SELECT dblink_exec('hostaddr=xxx.xx.xxx.xxx port=5432 dbname=bdname user=myuser password=mypass connect_timeout=2',
'UPDATE table1 SET field2 = field2 + 3.0 WHERE field1 = 16436') AS result;

更新

我創建了一個字符串,其中兩個查詢以;分隔; 正如評論所暗示的那樣

更新

我已經嘗試過 JDBC 原子事務作為 java 中的代碼。 我強制第二個 sql 失敗,但即使我指定 .setAutoCommit(false); dblink 通過第一個查詢影響了另一個數據庫。 我在沒有 dblink 事務的情況下嘗試了相同的代碼,並且回滾效果很好。 dblink 是問題所在。

更新

public static boolean ejecutarTransaccionDblink(String sql) {
    boolean estado = false;
    try {
        Statement sentencia = conexion.createStatement();
        conexion.setAutoCommit(false);
        if (sql.length() != 0) {
            if (sentencia.execute(sql)) {
                conexion.commit();
                estado = true;
            }
        }
    } catch (SQLException ex) {
        System.out.println(ex.toString());
        try {
            estado = false;
            conexion.rollback();
        } catch (SQLException ex1) {
        }
    } finally {
        try {
            conexion.setAutoCommit(true);
            return estado;
        } catch (SQLException ex) {
            return estado;
        }
    }
}

謝謝你的幫助。

為了在事務中運行查詢,您只需要在連接上將auto-commit功能設置為 false(記住在完成后將其設置回 true,尤其是從連接池中檢索連接時 -因此被重用)。

代碼比較簡單:

ResultSet resultado = null;
String statement1 = "SELECT dblink_exec('hostaddr=xxx.xx.xxx.xxx port=5432 dbname=bdname user=myuser password=mypass connect_timeout=2','INSERT INTO table3(field4) VALUES (5)') AS result";
String statement2 = "SELECT dblink_exec('hostaddr=xxx.xx.xxx.xxx port=5432 dbname=bdname user=myuser password=mypass connect_timeout=2','UPDATE table1 SET field2 = field2 + 3.0 WHERE field1 = 16436') AS result";
    try {
        // set auto-commit to false, to indicate start of transaction
        conexion.setAutoCommit(false);

        // run whatever queries you want on the connection, in a transaction, e.g. :
        Statement sentencia = conexion.createStatement();
        resultado = sentencia.executeQuery(sql);

        //manually commit the transaction when you're done
        conexion.commit();

        return resultado;
    } catch (SQLException ex) {
        System.out.println("Error Consulta:" + ex);

        // ensure transaction is rolled-back in case of error. (note: you might want to add an NPE check here
        con.rollback();
        return null;
    } finally {
        // close any statements / preparedStatements, etc. Note you MUST do this in the finally block, to ensure your connection won't stay in transaction.
        con.setAutoCommit(true);
    }

希望有幫助

更新

正如@a_horse_with_no_name 指出的那樣,dblink_exec 連接到遠程數據庫,因此上述內容並不完整,因為它僅處理第一個數據庫中的事務。

我相信答案應該在於使用帶有dblink_exec命名連接,其中該過程涉及:

  • 使用dblink_connect打開一個新連接
  • 使用dblink_exec在新命名連接中啟動事務
  • 在先前打開的連接中使用dblink_exec執行查詢 1
  • 在先前打開的連接中使用dblink_exec執行查詢 2
  • 在先前打開的連接中提交事務

因此,代碼如下所示:

SELECT dblink_connect('myconn','hostaddr=xxx.xx.xxx.xxx port=5432 dbname=bdname user=myuser password=mypass connect_timeout=2');
SELECT dblink_exec('myconn','BEGIN');
SELECT dblink_exec('myconn', 'INSERT INTO table3(field4) VALUES (5)');
SELECT dblink_exec('myconn', 'UPDATE table1 SET field2 = field2 + 3.0 WHERE field1 = 16436');
SELECT dblink_exec('myconn','COMMIT');

問題是,這一切都未經測試,所以@KazMiller 你能試試嗎?

如果所有其他方法都失敗了,請使用一個或多個CTE 將多個 SQL 命令鏈接為一個:

WITH upd AS (
  UPDATE table1 SET field2 = field2 + 3.0 WHERE field1 = 16436
  )
INSERT INTO table3(field4) 
VALUES (5)') AS result;

還是先INSERT ,無所謂。 通常以這種方式鏈接兩個不相關的命令是沒有意義的,但它是該功能的一個干凈的應用程序。 您可以通過這種方式鏈接任意數量的命令。 您不能將兩個命令寫入同一 您甚至可以讓最終的SELECT返回相關或不相關的值。 與 CTE 中的SELECT不同,所有修改數據的 CTE 總是執行完成。 手冊

WITH中的數據修改語句只執行一次,並且總是完成,與主查詢是否讀取所有(或實際上任何)輸出無關。 請注意,這WITH SELECT規則不同:如上一節所述,僅在主查詢需要其輸出時才會執行SELECT

有關的:


另一種選擇是在目標服務器上創建一個函數LANGUAGE sqlLANGUAGE plpgsql - 但任何語言都應該這樣做)以在單個事務中封裝任意數量的命令:

CREATE OR REPLACE FUNCTION f_wrapper()
  RETURNS void AS
$func$
   UPDATE table1 SET field2 = field2 + 3.0 WHERE field1 = 16436;
   INSERT INTO table3(field4) VALUES (5);
$func$ LANGUAGE sql;

然后:

SELECT dblink_exec('hostaddr=xxx.xx.xxx.xxx port=5432 dbname=bdname user=myuser password=mypass connect_timeout=2',
'SELECT f_wrapper()') AS result;

您可以即時創建(和刪除)一個函數,也可以保留一個接受值參數的函數。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM