0

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

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 但在我看来,返回值意味着第一次 delete 出现异常,但第二次 delete 被执行并提交。有人可以解释一下这个功能的含义吗?下面是函数和调用它的代码

    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)。为什么旧代码总是返回 1,即使它从 raccomandata_out 中删除了记录?

4

3 回答 3

3

由于函数返回 1,这表明第一次删除正在引发异常。这导致控制被切换到EXCEPTION块,它只是返回。第一次删除后的任何其他代码都不应执行。

异常处理程序很可怕,因为它会捕获任何异常,将其丢弃,并返回一个标志值,该值很少告诉您发生了什么。它只比WHEN OTHERS THEN NULL;. 写到这里,你无法知道发生了什么异常。异常处理程序应该被删除(以便调用代码可以以某种方式捕获和处理异常),或者重写以至少以某种方式记录实际的异常消息(SQLERRM)。

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

于 2011-03-30T14:56:18.610 回答
2

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

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

于 2011-03-30T14:55:11.683 回答
1

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

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

You also said rollback would be called on exception, and you have utx.commit(), but that's redundant with a commit in the function too.

于 2011-03-30T15:45:33.510 回答