回滚@Oracle 上的存储过程

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3490300/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 21:16:17  来源:igfitidea点击:

Rollback @ Stored Procedure on Oracle

oraclestored-procedurestransactionsrollback

提问by alex

I don't know if thats right but for some reason my stored procedure is not rolling back after an exception occurs. So my insert statement is commited even when i get an exception

我不知道这是否正确,但由于某种原因,我的存储过程在发生异常后没有回滚。所以即使我遇到异常,我的插入语句也会被提交

Did i forgot something?

我忘记了什么吗?

PROCEDURE SP_USUARIO_INSERT
        (
          pUSU_IDUSUARIO          IN OUT ENG.USU_USUARIO.USU_IDUSUARIO%TYPE,
          pUSU_CDUSUARIO          IN ENG.USU_USUARIO.USU_CDUSUARIO%TYPE,
          pPES_IDPESSOA           IN ENG.USU_USUARIO.PES_IDPESSOA%TYPE,
          pUSU_DLSENHA            IN ENG.USU_USUARIO.USU_CDUSUARIO%TYPE,
          pUSU_DLOBSERVACAO       IN ENG.USU_USUARIO.USU_DLOBSERVACAO%TYPE,
          pUSU_NUIP               IN ENG.USU_USUARIO.USU_NUIP%TYPE,
          pUSU_DTCADASTRO         IN ENG.USU_USUARIO.USU_DTCADASTRO%TYPE,
          pUSU_DTDESATIVACAO      IN ENG.USU_USUARIO.USU_DTDESATIVACAO%TYPE,
          pUSU_DTULTIMOACESSO     IN ENG.USU_USUARIO.USU_DTULTIMOACESSO%TYPE,
          pUSU_DLMAQUINA          IN ENG.USU_USUARIO.USU_DLMAQUINA%TYPE,
          pUSU_STNOVO             IN ENG.USU_USUARIO.USU_STNOVO%TYPE,
          pUSU_STATIVO            IN ENG.USU_USUARIO.USU_STATIVO%TYPE
        )
IS
sCreateUser Varchar(200);
bUsuarioExiste Number;
eUsuarioExiste Exception;
BEGIN
       SELECT 
               COUNT(usu_cdusuario) 
               INTO bUsuarioExiste 
        FROM ENG.USU_USUARIO 
        WHERE USU_CDUSUARIO = pUSU_CDUSUARIO;

        IF(bUsuarioExiste > 0) THEN
              RAISE eUsuarioExiste;
        END IF;

        SELECT usu_seq.nextval INTO pUSU_IDUSUARIO FROM DUAL;

        INSERT INTO ENG.USU_USUARIO
             (
                USU_IDUSUARIO, 
                USU_CDUSUARIO, 
                PES_IDPESSOA, 
                USU_DLOBSERVACAO, 
                USU_NUIP, 
                USU_DTCADASTRO, 
                USU_DTDESATIVACAO, 
                USU_DTULTIMOACESSO, 
                USU_DLMAQUINA, 
                USU_STNOVO, 
                USU_STATIVO
             )
        VALUES
             (
                pUSU_IDUSUARIO, 
                pUSU_CDUSUARIO, 
                pPES_IDPESSOA, 
                pUSU_DLOBSERVACAO, 
                pUSU_NUIP, 
                sysdate, 
                pUSU_DTDESATIVACAO, 
                pUSU_DTULTIMOACESSO, 
                pUSU_DLMAQUINA, 
                pUSU_STNOVO, 
                pUSU_STATIVO 
             ) ;
        sCreateUser := 'CREATE USER ' || pUSU_CDUSUARIO || ' IDENTIFIED BY ' || pUSU_DLSENHA;
        EXECUTE IMMEDIATE sCreateUser;
        EXECUTE IMMEDIATE 'GRANT ENG_GERAL TO ' || pUSU_CDUSUARIO;
        COMMIT;
EXCEPTION
       WHEN eUsuarioExiste THEN
             RAISE_APPLICATION_ERROR (-20001, 'Usuário já existe ou possui nome inválido.');
             ROLLBACK;
       WHEN OTHERS THEN
             RAISE_APPLICATION_ERROR (-20001, SQLCODE || ': ' || SQLERRM);
             ROLLBACK;
END SP_USUARIO_INSERT;

采纳答案by Gary Myers

I suspect you are 'handling' (or more accurately, ignoring) your exception.

我怀疑您正在“处理”(或更准确地说,忽略)您的异常。

create table temp (id number);
DECLARE
  v_str VARCHAR2(2);
BEGIN
  INSERT INTO temp VALUES (1);
  v_str := '123';
EXCEPTION
  WHEN VALUE_ERROR THEN DBMS_OUTPUT.PUT_LINE('Whoops');
END;
/
select * from temp;

Will show the row because, as far as the SQL layer is concerned, the procedure completed successfully (as the exception was caught and ignored).

将显示该行,因为就 SQL 层而言,该过程成功完成(因为异常被捕获并被忽略)。

There can be other reasons such as

可能还有其他原因,例如

  • The insert happens before the procedure execution and so isn't rolled back when the statement fails (and you don't explicitly rollback the transaction)
  • The insert is committed by a explicit commit before the exception is raised.
  • 插入发生在过程执行之前,因此在语句失败时不会回滚(并且您没有显式回滚事务)
  • 在引发异常之前通过显式提交提交插入。

回答by dpbradley

The "EXECUTE IMMEDIATE sCreateUser;" is implicitly committing your insert.

“立即执行 sCreateUser;” 正在隐式提交您的插入。

回答by Adam Musch

I think if you restructure your code, you can get the behavior you want.

我认为如果你重组你的代码,你可以获得你想要的行为。

PROCEDURE SP_USUARIO_INSERT      
        (      
          pUSU_IDUSUARIO          IN OUT ENG.USU_USUARIO.USU_IDUSUARIO%TYPE,      
          pUSU_CDUSUARIO          IN ENG.USU_USUARIO.USU_CDUSUARIO%TYPE,      
          pPES_IDPESSOA           IN ENG.USU_USUARIO.PES_IDPESSOA%TYPE,      
          pUSU_DLSENHA            IN ENG.USU_USUARIO.USU_CDUSUARIO%TYPE,      
          pUSU_DLOBSERVACAO       IN ENG.USU_USUARIO.USU_DLOBSERVACAO%TYPE,      
          pUSU_NUIP               IN ENG.USU_USUARIO.USU_NUIP%TYPE,      
          pUSU_DTCADASTRO         IN ENG.USU_USUARIO.USU_DTCADASTRO%TYPE,      
          pUSU_DTDESATIVACAO      IN ENG.USU_USUARIO.USU_DTDESATIVACAO%TYPE,      
          pUSU_DTULTIMOACESSO     IN ENG.USU_USUARIO.USU_DTULTIMOACESSO%TYPE,      
          pUSU_DLMAQUINA          IN ENG.USU_USUARIO.USU_DLMAQUINA%TYPE,      
          pUSU_STNOVO             IN ENG.USU_USUARIO.USU_STNOVO%TYPE,      
          pUSU_STATIVO            IN ENG.USU_USUARIO.USU_STATIVO%TYPE      
        )      
IS      
sCreateUser Varchar(200);      
bUsuarioExiste Number;      
eUsuarioExiste Exception;      
l_sqlcode      number;
BEGIN      
       SELECT       
               COUNT(usu_cdusuario)       
               INTO bUsuarioExiste       
        FROM ENG.USU_USUARIO       
        WHERE USU_CDUSUARIO = pUSU_CDUSUARIO;      

        IF(bUsuarioExiste > 0) THEN      
              RAISE eUsuarioExiste;      
        END IF;      

        sCreateUser := 'CREATE USER ' || pUSU_CDUSUARIO || ' IDENTIFIED BY ' ||     
                       pUSU_DLSENHA;      
        EXECUTE IMMEDIATE sCreateUser;      

        begin
              EXECUTE IMMEDIATE 'GRANT ENG_GERAL TO ' || pUSU_CDUSUARIO;      

              -- moved this into the insert          
              --SELECT usu_seq.nextval INTO pUSU_IDUSUARIO FROM DUAL;      

              INSERT INTO ENG.USU_USUARIO      
              (      
                   USU_IDUSUARIO,       
                   USU_CDUSUARIO,       
                   PES_IDPESSOA,       
                   USU_DLOBSERVACAO,       
                   USU_NUIP,       
                   USU_DTCADASTRO,       
                   USU_DTDESATIVACAO,       
                   USU_DTULTIMOACESSO,       
                   USU_DLMAQUINA,       
                   USU_STNOVO,       
                   USU_STATIVO      
              )      
              VALUES      
              (      
                   usu_seq.nextval,  --pUSU_IDUSUARIO,       
                   pUSU_CDUSUARIO,       
                   pPES_IDPESSOA,       
                   pUSU_DLOBSERVACAO,       
                   pUSU_NUIP,       
                   sysdate,       
                   pUSU_DTDESATIVACAO,       
                   pUSU_DTULTIMOACESSO,       
                   pUSU_DLMAQUINA,       
                   pUSU_STNOVO,       
                   pUSU_STATIVO       
              ) 
              returning ;      
        exception
          when others then 
             -- save off the sqlcode, you will need it to reraise
             l_sqlcode := SQLCODE;
             -- rollback any insert that may have run, 
             -- depending on where the exception was raised
             rollback;
             -- drop the created user
             execute immediate 'drop user ' || pUSU_CDUSUARIO ;
             -- reraise the error
             RAISE_APPLICATION_ERROR (-20001, l_sqlcode || ': ' || 
                                      SQLERRM(-l_sqlcode));                   
        end;

        COMMIT;      
EXCEPTION      
       WHEN eUsuarioExiste THEN      
             RAISE_APPLICATION_ERROR (-20001, 
                         'Usuário já existe ou possui nome inválido.');      
             ROLLBACK;      
       WHEN OTHERS THEN      
             RAISE_APPLICATION_ERROR (-20001, 
                 SQLCODE || ': ' || SQLERRM);      
             ROLLBACK;      
END SP_USUARIO_INSERT;   

回答by Alex Poole

Doesn't the RAISE_APPLICATION_ERRORmean that the ROLLBACKis unreachable? Unless the caller is also issuing a ROLLBACK, I think those need to be the other way around. You may be getting in implicit commit from DDL as other have said, depending on what's actually erroring, so it might be irrelevant, but it doesn't look quite right.

这不RAISE_APPLICATION_ERROR意味着ROLLBACK无法访问吗?除非调用者也发出ROLLBACK,否则我认为这些需要相反。正如其他人所说,您可能会从 DDL 获得隐式提交,具体取决于实际出错的内容,因此它可能无关紧要,但看起来不太正确。