2016-04-29 3 views
3

Я работаю в приложении Java, где мне нужно выполнить эти два запроса (как строки в java) одновременно и откат транзакции, если там есть ошибки.Выполнять несколько запросов как одну транзакцию dblink

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

Я создал одну строку с двумя запросами, разделенных ; как в комментариях предлагают

UPDATE

Я пытался JDBC атомарных транзакций как код в java. Я заставляю второй sql сбой, но даже если я указываю .setAutoCommit (false); dblink повлиял на другую базу данных с первым запросом. Я пробовал один и тот же код с транзакциями NO dblink, и откат работает хорошо. Проблема dblink.

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

Спасибо за вашу помощь.

+0

JDBC предоставляет атомарные операции с помощью пакетов: https://docs.oracle.com/javase/tutorial/jdbc/basics/transactions.html –

+0

@EricN Я понимаю, что, но он не работает с bdlink –

+0

Вы попробуйте отправить оба оператора (разделенные символом ';') одним вызовом 'dblink_exec()' –

ответ

1

Чтобы выполнить запросы в транзакции, вам просто нужно установить для функции 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); 
    } 

Надежда, что помогает

UPDATE

Как @a_horse_with_no_name отметил, dblink_exec подключается к удаленному дб, поэтому выше не является полным , поскольку он обрабатывает транзакции только в первом db.

Я считаю, что ответ должен лежать с помощью названных соединений с dblink_exec где процесс включает в себя:

  • открывает новое соединение с dblink_connect
  • начинает транзакцию в новом имени связи с dblink_exec
  • выполнение запроса 1 с помощью dblink_exec в ранее открытом соединении
  • выполнение запроса 2 с dblink_exec в ранее открытом связи
  • совершение сделки в ранее открытой связи

Таким образом, код будет выглядеть следующим образом:

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 ты мог пожалуйста, попробуйте?

+1

Но 'dblink_exec()' откроет другое соединение (и, следовательно, другую транзакцию) удаленному хосту, независимо от соединения JDBC (и его автоматической фиксации), который использует код Java –

+0

@a_horse_with_no_name хорошая точка! Полностью упущено, что dblink_exec фактически подключается к удаленному db .. будет обновлять ответ соответственно – gsaslis

+0

обновленный ответ выше с другим подходом, после комментария @a_horse_with_no_name ... Kaz, не могли бы вы попробовать попробовать? – gsaslis

1

Если все остальное терпит неудачу, цепочка множественным SQL команды в одну, используя один или несколько КТР:

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

Или INSERT первый, не имеет значения. Как правило, это не имеет смысла цепочки двух несвязанных команд таким образом, но это чистое применение этой функции. Вы можете объединить столько команд, сколько вам нравится. У вас просто не может быть двух команд, записывающих одну и ту же строку . Вы можете даже иметь окончательные SELECT возвращаемые связанные или несвязанные значения. В отличие от SELECT в CTE, все модифицирующие данные CTE всегда выполняются до завершения. The manual:

данных модифицирующие заявления в WITH выполняются ровно один раз, и всегда к завершению, независимо от того первичного запроса читает все (или действительно любой) своей продукции. Обратите внимание, что это не так: из правила для SELECT в WITH: как указано в предыдущем разделе, Выполнение SELECT осуществляется только в том случае, если основной запрос требует его вывода.

Похожие:


Другим вариантом было бы создать функцию (LANGUAGE sql или LANGUAGE 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; 

Вы можете создать (и падение) функцию на лету или сохраняться тот, который принимает параметры для значений.

Смежные вопросы