2016-04-29 26 views
3

Ich arbeite in einer Java-Anwendung, wo ich diese beiden Abfragen (als Strings in Java) gleichzeitig ausführen und die Transaktion zurücksetzen muss, wenn es Fehler gab.Mehrere Abfragen als eine dblink-Transaktion ausführen

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; 

UPDATE

habe ich einen String mit den beiden mit ; wie in den Kommentaren getrennt Abfragen vorschlagen

UPDATE

Ich habe als Code atomare Transaktionen versucht JDBC in Java. Ich erzwinge den zweiten SQL-Fehler, aber selbst wenn ich .setAutoCommit (false); Der dblink hat die andere Datenbank mit der ersten Abfrage beeinflusst. Ich habe den gleichen Code mit NO dblink Transaktionen versucht und der Rollback funktioniert gut. dblink ist das Problem.

Java UPDATE

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; 
     } 
    } 
} 

Vielen Dank für Ihre Hilfe.

+0

JDBC atomare Transaktionen über Chargen gibt: https://docs.oracle.com/javase/tutorial/jdbc/basics/transactions.html –

+0

@EricN ich verstehen, aber es funktioniert nicht mit bdlink –

+0

Haben Sie versucht, das Senden beide Anweisungen (getrennt durch ein ';') mit einem einzigen Aufruf von 'dblink_exec()' –

Antwort

1

Um die Abfragen in einer Transaktion auszuführen, müssen Sie einfach die Funktion auto-commit auf der Verbindung auf false setzen (denken Sie daran, es zurückzusetzen, wenn Sie fertig sind, besonders wenn die Verbindung von einer Verbindung abgerufen wird Pool - und daher wiederverwendet).

Der Code ist relativ einfach:

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); 
    } 

Hoffnung, die

UPDATE

Wie @a_horse_with_no_name wies darauf hin, hilft, verbindet dblink_exec zu einem entfernten db, so dass die oben ist nicht komplett , da es nur Transaktionen in der ersten db behandelt.

Ich glaube, die Antwort mit der Verwendung von benannten Verbindungen mit dblink_exec liegen sollte, wo der Prozess umfasst:

  • Öffnen Sie eine neue Verbindung mit dblink_connect
  • Starten einer Transaktion in neue benannte Verbindung mit dblink_exec
  • Ausführen von Abfrage 1 mit dblink_exec in zuvor geöffneter Verbindung
  • Ausführen von Abfrage 2 mit dblink_exec in zuvor geöffnete Verbindung
  • Transaktion

daher in zuvor geöffnete Verbindung zu begehen, würde der Code wie folgt aussehen:

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'); 

Die Sache ist, das alles ungetestet ist, so @KazMiller konnte man Bitte probieren Sie es aus.

+1

Aber 'dblink_exec()' wird eine andere Verbindung (und damit eine andere Transaktion) auf den Remote-Host unabhängig von der JDBC-Verbindung (und seine automatische Festschreibungseinstellung) der Java-Code verwendet –

+0

@a_horse_with_no_name guter Punkt! Völlig verpasst, dass dblink_exec tatsächlich mit remote db verbindet .. wird die Antwort entsprechend aktualisieren – gsaslis

+0

aktualisierte Antwort oben mit einer anderen Herangehensweise, nach @a_horse_with_no_name 'Kommentar ... Kaz, könntest du es bitte versuchen? – gsaslis

1

Wenn alles andere fehlschlägt, Kette mehrere SQL-Befehle in eine mit einem oder mehreren CTEs:

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

Oder INSERT zunächst keine Rolle spielt. Normalerweise würde es keinen Sinn machen, zwei nicht verwandte Befehle auf diese Weise zu verketten, aber es ist eine saubere Anwendung der Funktion. Sie können so viele Befehle verketten, wie Sie möchten. Sie können einfach nicht zwei Befehle schreiben, die in dieselbe Zeile Zeile schreiben. Sie können sogar eine abschließende SELECT related oder unrelated Werte haben. Im Gegensatz zu SELECT in CTEs werden alle datenmodifizierenden CTEs immer vollständig ausgeführt. The manual:

Daten modifizierende Aussagen in WITH genau einmal ausgeführt, und immer bis zur Fertigstellung, unabhängig davon, ob die primären Abfrage liest all (oder in der Tat überhaupt) ihre Produktion. Beachten Sie, dass das von der Regel für SELECT in WITH in SELECT nur so weit ausgeführt wird, wie die primäre Abfrage seine Ausgabe fordert.

Verwandte:


Eine weitere Möglichkeit, eine Funktion zu schaffen wäre (LANGUAGE sql oder LANGUAGE plpgsql - aber jede Sprache tun sollte) auf dem Zielserver kapseln eine beliebige Anzahl von Befehlen in einer einzigen Transaktion ein:

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; 

Dann:

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

Sie können erstellen (und Drop) eine Funktion im laufenden Betrieb oder eine fortbestehen, die Parameter für Werte annimmt.