如何编写oracle函数来更新不同模式的表

时间:2011-03-30 14:47:38

标签: java oracle plsql atomikos

您好 我正在将一个应用程序移植到tomcat,我必须在同一台服务器上运行两个数据库,并且我已经将tomcat与atomikos transactionessential集成在一起。这是我与jta的第一个项目,oracle dba告诉我我不需要xa和两阶段提交,因为模式在同一台服务器上。所以我使用了非xa方法与atomikos。 单个模式上的以下代码工作正常(按预期提交和回滚):

utx.begin();
conn = //get connection 
if (sAzione.equals("1")) 
sql = "UPDATE parametri set valore =to_char(sysdate,'dd/mm/yyyy HH24:MI:ss') where id_parametri= 9 ";
//execute query
sql = "SELECT SEQ_LOTTO.nextval id FROM dual";
    //other operations
sql = "INSERT INTO LOTTO (id_lotto, numero_lotto, id_area, id_stato_lavorazione, id_autore, id_tipo)";
sql = sql + " VALUES (" + id + ", " + numero + ", " + request.getParameter("idArea") + ",1,"+ session.getAttribute("id_anagrafica")+ "," + request.getParameter("idTipo") + ")";
//execute import and release connection
utx.commit();

在另一个地方,调用以下oracle函数并尝试更改两个模式,并返回代码1。我不知道pl-slq,但在我看来,返回值意味着第一次删除时出现异常,但第二次删除被执行并提交。有人可以解释一下这个功能的含义吗?下面是函数和调用它的代码

    create or replace FUNCTION FN_ELIMINA_RACC (idracc IN NUMBER, idlotto IN NUMBER)
   RETURN NUMBER
IS
   retvalue   NUMBER (1);
BEGIN
   retvalue := 1;

   DELETE FROM npa_collaudo.documento_raccomandata
         WHERE id_raccomandata = idracc;

   retvalue := 2;

   DELETE FROM raccomandata_out
         WHERE id_racc_out = idracc;

   retvalue := 3;

   IF idlotto != 0
   THEN
      UPDATE lotto
         SET numero_racc = numero_racc - 1
       WHERE id_lotto = idlotto;
   END IF;

   retvalue := 0;
   COMMIT;
   RETURN retvalue;
EXCEPTION
   WHEN OTHERS
   THEN
      RETURN retvalue;
END;

//the calling code
    utx.begin();
         //get connection
         sql = "FN_ELIMINA_RACC(" + idRacc + ", " + idLotto + ");";
                ret = connessioneDB.eseguiSP(sql);
         if (!(ret == 0)){
    throw new Exception("exception");
    utx.commit();
//since it returns 1 an exception is raised and rollback gets called

提前感谢您的任何帮助

编辑:进一步调查这个(可怕的)代码,并且由于你的答案,我发现这成了臭名昭着的“eseguiSP”:

//strSQL is "FN_ELIMINA_RACC(..." 
DBOracle dbType = new DBOracle();
String SQL = "";
int retValue = 0;
SQL = " DECLARE ";
SQL = SQL + " ret NUMBER; ";
SQL = SQL + " BEGIN ";
SQL = SQL + " ret := " + strSQL; 
SQL = SQL + " END; ";
try {
stmt = conn.prepareCall(SQL);
retValue = stmt.executeUpdate(SQL); 
} catch (SQLException e) {
//retValue = false;
}
return retValue;

我已将其更改为:

c = ds.getConnection();
java.sql.CallableStatement cstmt = c.prepareCall("{?=call FN_ELIMINA_RACC(?,?)}");
cstmt.registerOutParameter(1,java.sql.Types.INTEGER);
cstmt.setInt(2, idRacc);
cstmt.setInt(3, idLotto);
cstmt.execute();
ret = cstmt.getInt(1);

现在它工作正常(或者至少返回0)。为什么即使从raccomandata_out中删除了记录,旧的代码总是返回1?

3 个答案:

答案 0 :(得分:3)

由于函数返回1,这表示第一次删除正在抛出异常。这会导致控制切换到EXCEPTION块,而这只是返回。第一次删除后的其他代码都不应该执行。

异常处理程序非常糟糕,因为它捕获任何异常,丢弃它,并返回一个标志值,告诉你很少发生的事情。它只比WHEN OTHERS THEN NULL;略好一点。在编写本文时,您无法知道发生了什么异常。应该删除异常处理程序(以便调用代码可以以某种方式捕获和处理异常),或者重写为至少以某种方式记录实际的异常消息(SQLERRM)。

最明显的猜测是引发异常,因为执行代码的模式对其他模式中的表没有删除访问权限。可能相关的一个Oracle怪癖是存储的PL / SQL代码(例如此函数)无法利用通过角色授予的访问权限。必须直接向用户授予对其他模式对象的任何访问权。

答案 1 :(得分:2)

此过程中的异常处理程序不是特别有用。它完全隐藏了Oracle正在抛出的错误消息。如果完全消除异常处理程序,那么错误堆栈是什么?

我的猜测是该过程的所有者没有权限从npa_collaudo.documento_raccomandata表中删除行。但是,如果不知道实际出现了什么异常,就不可能知道。

答案 2 :(得分:1)

你怎么知道函数返回1?您抛出的异常不会报告ret值。调用本身可能已被破坏 - 请尝试从;字符串中删除尾随的sql。虽然你应该从eseguiSP(sql)获得一个更有用的例外,但是它可能隐藏在你的代码的其他地方(可能还有一些事情是添加一些让它看起来像是1的东西?);并且删除都不会生效,除非它试图将其视为两个命令,并且仅在它看到第二个为空时才抱怨。这听起来不太可能,但你永远不会知道,所以无论如何我都会尝试删除分号。

此外,您可能应该使用绑定参数进行调用,而不是将值嵌入sql

你还说过将在异常时调用回滚,你有utx.commit(),但这也是函数中提交的冗余。