作为一个dblink事务执行多个查询

时间:2016-04-29 20:36:16

标签: java postgresql dblink

我正在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使用第一个查询影响了另一个数据库。我使用NO dblink事务尝试了相同的代码,并且回滚效果很好。 dblink就是问题所在。

Java更新

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

感谢您的帮助。

2 个答案:

答案 0 :(得分:1)

为了在事务中运行查询,您只需要在连接上将"data3"功能设置为false(记住在完成后将其设置为true,尤其是在检索连接时)来自连接池 - 因此重用)。

代码相对简单:

auto-commit

希望有所帮助

更新

正如@a_horse_with_no_name指出的那样,dblink_exec连接到远程数据库,因此上面的内容不完整,因为它只处理第一个数据库中的事务。

我认为答案应该是将命名连接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); } 一起使用,其中涉及以下过程:

  • 使用dblink_exec
  • 打开新连接
  • 使用dblink_connect
  • 在新的命名连接中启动事务
  • 在之前打开的连接中使用dblink_exec执行查询1
  • 在先前打开的连接中使用dblink_exec执行查询2
  • 在之前打开的连接中提交交易

因此,代码如下所示:

dblink_exec

问题是,这都是未经测试的,所以@KazMiller可以试试这个吗?

答案 1 :(得分:1)

如果所有其他方法都失败了,请使用一个或多个 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始终执行完成。 The manual

  

WITH中的数据修改语句只执行一次,并且   始终完成,与主查询是否读取无关   所有(或实际上任何)他们的输出。请注意,这是不同的   来自SELECTWITH的规则:如上一节所述,   只有主查询才会执行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;

您可以动态创建(并删除)一个函数,或者保留一个带有值参数的函数。