如何在程序内使用乐观锁定?

时间:2018-11-11 13:59:40

标签: oracle plsql

我使用触发器实现了乐观锁,如何在过程中不使用触发器的情况下做到这一点?

我读了这篇文章Implementic Optimistic Locking

但是我不明白如何在程序中实现它。

我尝试了这个,但没有达到我的期望

SQL> CREATE OR REPLACE PACKAGE BODY account_api AS
  2       PROCEDURE upd_account
  3          (p_acc_id     accounts.acc_id%type
  4            , p_acc_name   accounts.acc_name%type
  5            , p_acc_amount accounts.acc_amount%type
  6            , p_acc_date   accounts.acc_date%type
  7          , p_acc_version accounts.acc_version%type
  8          )
  9           IS
 10         BEGIN
 11             UPDATE accounts
 12             set acc_name    = acc_name
 13           , acc_amount  = acc_amount
 14           , acc_date    = acc_date
 15           , acc_version = acc_version + 1
 16     where acc_id   = p_acc_id
 17     and  acc_version = p_acc_version;
 18  if(SQL%ROWCOUNT = 0)
 19  THEN
 20  RAISE_APPLICATION_ERROR( -20001, 'Oops, the row has changed since you read it.' );
 21  END IF;
 22   END;
 23   end account_api;
 24  /

SQL> begin
  2  account_api.upd_account(1, 'user12', 1200, sysdate, '11-NOV-18 06.10.01.660948 AM');
  3   end;
  4  /
PL/SQL procedure successfully completed.

我正在尝试使用相同的时间戳记的代码,并且现在已经完成

   SQL> begin
      2  account_api.upd_account(1, 'user1', 1200, sysdate, '11-NOV-18 06.10.01.660948 AM');
      3   end;
      4  /
    PL/SQL procedure successfully completed.

2 个答案:

答案 0 :(得分:2)

这个想法是,您将首先选择一条记录(通过select查询),然后决定对其进行更新。您引用的过程是正确的。它要求您将acc_version的值作为最后一个参数传递给它。您可以从查询的记录中获得该值。

这是您必须遵循的一种合同:您需要查询acc_version,然后将其传递给要进行更新的过程。每次更新后,如果仍然需要进行更多更新,则必须重新查询acc_version的当前值。

app_version字段必须是数字(而不是日期)。它旨在反映记录的版本,例如版本1、2、3,...,这可以看作是对该特定记录进行的更新次数。

该过程将在以下条件下进行更新: ,即记录中的此值在此期间未更新(通过其他更新)。它使用简单的where子句进行检查。

如果更新不更新任何内容,则意味着记录不再满足此条件(并且已被更改)。在这种情况下,会引发异常。

但是,如果acc_version仍在传递给过程时仍然有效,则update语句确实会更新目标记录。同时,update语句递增acc_version。这样可以防止已经在此更新之前 查询此记录的其他客户端进行更新。他们将需要重新查询记录以获得正确的acc_version值,然后重试。

答案 1 :(得分:0)

这是我想要实现的

   PROCEDURE upd_account
                    (   p_acc_id      accounts.acc_id%type
                      , p_acc_name    accounts.acc_name%type
                      , p_acc_amount  accounts.acc_amount%type
                      , p_acc_date    accounts.acc_date%type
                      , p_version     accounts.version%type
                    )
                     IS
                  BEGIN
                      UPDATE accounts
                      set acc_name    = p_acc_name
                    , acc_amount  = p_acc_amount
                    , acc_date    = p_acc_date
                    , version = p_version + 1
             where acc_id = p_acc_id
             and version = p_version;
             DBMS_OUTPUT.PUT_LINE ('Number of updated records: ' || TO_CHAR(SQL%ROWCOUNT));
          if(SQL%ROWCOUNT = 0)
          THEN
          RAISE_APPLICATION_ERROR( -20001, 'Oops, the row has changed since you read it.' );
          END IF;
           END;