13.2 LOBロケータとトランザクション境界

LOBロケータは、トランザクションとトランザクションIDの両方で使用できます。

関連項目:

LOBロケータの詳細は、LOB用のロケータ・インタフェースを参照してください

13.2.1 LOBロケータとトランザクション境界について

この項では、LOBロケータとトランザクション境界について学習します。

LOBロケータとトランザクションについては、次のことに注意してください。

  • ロケータがトランザクションIDを含む場合

    トランザクションを開始してからロケータを選択する場合: トランザクションを開始した後にロケータを選択すると、ロケータにトランザクションIDが含まれます。トランザクションを明示的に開始しなくてもトランザクションに暗黙的に入ることができます。たとえば、SELECT... FOR UPDATEによってトランザクションは暗黙的に開始されます。このような場合、ロケータにはトランザクションIDが含まれます。

  • ロケータがトランザクションIDを含まない場合

    • トランザクション外でロケータを選択する場合: 対照的に、トランザクションの外部でロケータを選択する場合、ロケータはトランザクションIDを含みません。

    • DML文の実行の前に選択した場合: トランザクションIDは、最初のDML文が実行されるまでは割り当てられません。このため、このようなDML文の前に選択されたロケータはIDを含みません。

13.2.2 ロケータを使用したLOBに対する読取りおよび書込み操作

ロケータがトランザクションIDを含んでいるかどうかにかかわらず、常にロケータを使用してLOBデータを読み取ることができます。この項では、その様々な側面について学習します。

  • ロケータを使用して書込みができない場合:

    ロケータがトランザクションIDを含む場合、その特定のトランザクション外でLOBに書き込むことはできません。

  • ロケータを使用して書込みができる場合:

    ロケータがトランザクションIDを含まない場合、トランザクションを明示的または暗黙的に開始した後、LOBに書き込むことができます。

  • シリアライズ可能なトランザクションでロケータを使用して読取りまたは書込みができない場合:

    ロケータが古いトランザクションのトランザクションIDを含み、現在のトランザクションがシリアライズ可能である場合、このロケータを使用して読取りまたは書込みを行うことはできません。

  • シリアライズ不能なトランザクションでロケータを使用して読取りはできるが書込みができない場合:

    トランザクションがシリアライズ不能である場合、トランザクション外で読み取ることはできますが、書き込むことはできません。

「トランザクション境界外でのロケータの選択」「トランザクション境界内でのロケータの選択」「複数のトランザクションにまたがることはできないLOBロケータ」および「トランザクションにまたがらないロケータの例」の各例は、ロケータとシリアライズ可能でないトランザクション間の関係を示しています

13.2.3 トランザクション境界外でのロケータの選択

この項では、トランザクション外でロケータが選択された場合に、シリアライズ可能でないトランザクションでロケータを使用する方法を説明する2つの使用例を示します。

シナリオ1:

  1. 現行トランザクションを持たないロケータを選択します。この時点ではロケータはトランザクションIDを含みません。

  2. トランザクションを開始します。

  3. ロケータを使用してLOBからデータを読み取ります。

  4. トランザクションをコミットまたはロールバックします。

  5. ロケータを使用してLOBからデータを読み取ります。

  6. トランザクションを開始します。ロケータはトランザクションIDを含みません。

  7. ロケータを使用してデータをLOBに書き込みます。この操作は、ロケータが書込みの前にトランザクションIDを含まないため有効です。このコールの後、ロケータはトランザクションIDを含むようになります。

シナリオ2:

  1. 現行トランザクションを持たないロケータを選択します。この時点ではロケータはトランザクションIDを含みません。
  2. トランザクションを開始します。ロケータはトランザクションIDを含みません。
  3. ロケータを使用してLOBからデータを読み取ります。ロケータはトランザクションIDを含みません。
  4. ロケータを使用してデータをLOBに書き込みます。この操作は、ロケータが書込みの前にトランザクションIDを含まないため有効です。このコールの後、ロケータはトランザクションIDを含むようになります。続けてLOBの読取りまたは書込みを行うことができます。
  5. トランザクションをコミットまたはロールバックします。ロケータは引き続きトランザクションIDを含みます。
  6. ロケータを使用してLOBからデータを読み取ります。この操作は有効です。
  7. トランザクションを開始します。ロケータは前のトランザクションのIDを含んでいます。
  8. ロケータを使用してデータをLOBに書き込みます。ロケータが現行トランザクションに一致するトランザクションIDを含まないため、この書込み操作は失敗します。

13.2.4 トランザクション境界内でのロケータの選択

この項では、トランザクション内でロケータが選択された場合に、シリアライズ可能でないトランザクションでロケータを使用する方法を説明する2つの使用例を示します。

シナリオ1:

  1. トランザクションの中でロケータを選択します。この時点では、ロケータはトランザクションIDを含んでいます。

  2. トランザクションを開始します。ロケータは前のトランザクションのIDを含んでいます。

  3. ロケータを使用してLOBからデータを読み取ります。ロケータの中のトランザクションIDは現在のトランザクションに一致していませんが、この操作は有効です。

    関連項目:

    ロケータを使用したLOBデータの読取りの詳細は、読取り一貫性のあるロケータを参照してください。

  4. ロケータを使用してデータをLOBに書き込みます。ロケータの中のトランザクションIDが現在のトランザクションに一致していないため、この操作は失敗します。

シナリオ2:

  1. トランザクションを開始します。
  2. ロケータを選択します。ロケータがトランザクションの中で選択されたため、トランザクションIDが含まれています。
  3. ロケータを使用してLOBの読取りまたは書込みを行います。これらの操作は有効です。
  4. トランザクションをコミットまたはロールバックします。ロケータは引き続きトランザクションIDを含みます。
  5. ロケータを使用してLOBからデータを読み取ります。ロケータの中にトランザクションIDがあり、そのトランザクションはすでにコミットまたはロールバックされていますが、この操作は有効です。
  6. ロケータを使用してデータをLOBに書き込みます。ロケータの中のトランザクションIDはすでにコミットまたはロールバックされたトランザクション用であるため、この操作は失敗します。

13.2.5 複数のトランザクションにまたがることはできないLOBロケータ

データの書込みに使用するLOBロケータは、複数のトランザクションにまたがることはできません。ただし、シリアライズ可能なトランザクション内でない場合は、ロケータを使用してLOB値を読み取ることができます。

DBMS_LOB、OCI、SQLのINSERT文またはUPDATE文を使用し、LOBロケータによって永続LOBの値を更新すると、読取り一貫性のあるロケータが更新済ロケータに変更されます。

INSERT文やUPDATE文によって、トランザクションが自動的に開始され、行がロックされます。一度これが発生すると、ロケータを現行トランザクション以外で使用して、LOB値を変更できなくなる可能性があります。データの書込みに使用するLOBロケータを複数のトランザクションにまたがって使用することはできません。ただし、シリアライズ可能なトランザクション内でない場合は、ロケータを使用してLOB値を読み取ることができます。

次のコード例では、clob_updatedというCLOBロケータが作成され、次の操作が実行されます。

  • 最初のSELECT INTOの実行時(t1)に、ad_sourcetext内の値がロケータclob_updatedに対応付けられます。

  • 次の操作で(t2)、DBMS_LOB.WRITEファンクションを使用してclob_updated内の値を変更します。DBMS_LOB.READが新しい値を示します。

  • COMMIT文(t3)によって現行のトランザクションが終了します。

  • トランザクションを終了すると(t4)、clob_updatedロケータは別のトランザクション(コミット済)を参照することになるため、その後のDBMS_LOB.WRITE操作は失敗します。これは、戻されるエラーによって通知されます。このLOBロケータをさらにDBMS_LOB(およびOCI)の変更操作で使用するには、再選択する必要があります。

13.2.6 トランザクションにまたがらないロケータの例

トランザクションにまたがらないロケータの例では、print_media表を使用します。

INSERT INTO PRINT_MEDIA VALUES (2056, 20010, EMPTY_BLOB(), 
    'abcd', EMPTY_CLOB(), EMPTY_CLOB(), NULL, NULL, NULL, NULL);

COMMIT;

DECLARE
  num_var          INTEGER;
  clob_updated     CLOB;
  read_amount      INTEGER;
  read_offset      INTEGER;
  write_amount     INTEGER;
  write_offset     INTEGER;
  buffer           VARCHAR2(20);

BEGIN
          -- At time t1:
     SELECT      ad_sourcetext
     INTO        clob_updated
     FROM        PRINT_MEDIA
     WHERE       ad_id = 20010
     FOR UPDATE;
     read_amount := 10;
     read_offset := 1;
     dbms_lob.read(clob_updated, read_amount, read_offset, buffer);
     dbms_output.put_line('clob_updated value: ' || buffer);
     -- This produces the output 'abcd'

     -- At time t2:
     write_amount := 3;
     write_offset := 5;
     buffer := 'efg';
     dbms_lob.write(clob_updated, write_amount, write_offset, buffer);
     read_amount := 10;
     dbms_lob.read(clob_updated, read_amount, read_offset, buffer);
     dbms_output.put_line('clob_updated value: ' || buffer);
     -- This produces the output 'abcdefg'

    -- At time t3:
    COMMIT;

     -- At time t4:
    dbms_lob.write(clob_updated , write_amount, write_offset, buffer);
    -- ERROR: ORA-22990: LOB locators cannot span transactions
END;
/