PL/SQLで「親トランザクションがROLLBACKされても絶対に残したい記録」を実装するには、PRAGMA AUTONOMOUS_TRANSACTIONが唯一の正解です。監査ログ・エラー記録・リトライカウンタ・レート制限など、「親が失敗しても確定させる必要がある」処理を呼び出し元と独立した別トランザクションで動かせる仕組みです。
ただし「便利だから何でも自律トランザクションにする」という濫用は本番障害の温床です。AUTONOMOUS_TRANSACTIONは親と完全に独立した別セッションのように振る舞うため、親と同じ表を更新するとデッドロック、COMMIT忘れでORA-06519、業務データに使うと整合性破壊といった重大事故を起こします。
この記事ではAUTONOMOUS_TRANSACTIONの正しい実装と運用を実戦コード中心で整理します。5つの具体ユースケース(監査ログ/エラー記録/リトライカウンタ/レート制限/キュー投入)の即実装可能コード、デッドロック設計、ORA-06519対策、SAVEPOINTとの使い分け、テスト方法、アンチパターン6選、FAQまで2026年版で解説します。
この記事でわかること
- AUTONOMOUS_TRANSACTIONの正しい使い所と典型ユースケース5種
- 監査ログAPIをパッケージ化した再利用可能な実装テンプレート
- 親が失敗してもエラー記録だけ残すエラーログパターン
- 親と同じ表を扱うときのデッドロック回避設計
- ORA-06519(COMMIT/ROLLBACK忘れ)の発生条件と対処
- SAVEPOINTとAUTONOMOUS_TRANSACTIONの決定的違いと使い分け
- JDBCトランザクションマネージャとの相互作用
- 独立トランザクションを使うときのテスト戦略
- 本番で踏むアンチパターン6選
30秒でわかるAUTONOMOUS_TRANSACTIONの結論
忙しい読者向けの結論先出しです。
| 結論 | 理由・効果 |
|---|---|
| ① 用途は監査ログ・エラー記録・カウンタ系に限定 | 業務データに使うと整合性破壊と運用ブラックボックス化 |
② PRAGMA AUTONOMOUS_TRANSACTIONは必ずCOMMITまたはROLLBACKで終わらせる |
忘れるとORA-06519が発生して呼び出しが失敗する |
| ③ 親と同じ表を更新しない | 親が保持中のロックと衝突しデッドロックになる |
| ④ 親のSELECT結果は子からは見えない | 独立トランザクションなのでREAD COMMITTED分離となる |
| ⑤ SAVEPOINTと混同しない | SAVEPOINTは同一TX内のしおり、自律TXは別TX |
| ⑥ ログAPIはパッケージ化して集中管理 | あちこちで個別実装するとPRAGMA忘れの事故源になる |
| ⑦ JDBCのトランザクション境界とは独立して動く | クライアント側のrollbackでも自律TXのコミットは生き残る |
動作の仕組み|呼び出し元との独立性
AUTONOMOUS_TRANSACTIONを宣言すると、そのプロシージャは呼び出されたとき親のトランザクションを一時停止し、新しい独立したトランザクションを開始します。内部でCOMMITまたはROLLBACKで終了したら親のトランザクションが再開する流れです。
独立しているもの/共有しているもの
「独立」という言葉から「完全に別セッション」と想像しがちですが、実際にはセッション固有のものは共有されています。正しい挙動を把握しましょう。
- 独立: トランザクション(COMMIT/ROLLBACK)・ロック・UNDO・SAVEPOINT
- 共有: セッションそのもの・パッケージ変数・SYS_CONTEXT・TEMPORARY TABLE(ON COMMIT PRESERVE ROWSの場合)・カーソル状態(呼出し元のは見える)
- 分離: 親が更新中の未コミットデータは子からは見えない(READ COMMITTED分離)
-- 親トランザクションが更新中のデータは子から見えない
CREATE OR REPLACE PROCEDURE child_proc IS
PRAGMA AUTONOMOUS_TRANSACTION;
v_balance NUMBER;
BEGIN
-- 親がUPDATEしたが未COMMITの値は見えない
SELECT balance INTO v_balance FROM accounts WHERE id = 1;
DBMS_OUTPUT.PUT_LINE('child sees: ' || v_balance); -- 親のUPDATE前の値
COMMIT;
END;
/
DECLARE
v_b NUMBER;
BEGIN
-- 親が更新する(未COMMIT)
UPDATE accounts SET balance = 9999 WHERE id = 1;
SELECT balance INTO v_b FROM accounts WHERE id = 1;
DBMS_OUTPUT.PUT_LINE('parent sees: ' || v_b); -- 9999(自分のセッション内)
-- 子を呼ぶ
child_proc; -- ← childは元の値を見る(READ COMMITTED)
ROLLBACK; -- 親はROLLBACK
END;
/
「親が更新中の値が子から見えない」のは独立トランザクションの本質的な性質です。これにより親と子で同じ行を読むタイミングで結果が違う事象が発生します。監査ログのように「自身のINSERTだけ独立コミットしたい」用途では問題ないですが、業務データを子で読み取って判断する設計では予期しない結果になるので注意してください。
典型ユースケース5種|即実装可能コード集
実務でAUTONOMOUS_TRANSACTIONが活きる5つのシナリオを即実装可能なコード付きで紹介します。いずれも「親が失敗しても必ず残したい・独立して動かしたい」処理です。
ユースケース1|監査ログ(共通ライブラリAPI)
すべての業務処理から呼び出される共通ログ書き込みパッケージ。個別箇所でPRAGMA宣言を書くのではなく、1本のパッケージに集約して再利用するのがベストプラクティスです。
-- ログ表
CREATE TABLE audit_log(
log_id NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
log_ts TIMESTAMP DEFAULT SYSTIMESTAMP,
who VARCHAR2(64) DEFAULT USER,
module VARCHAR2(64),
level_cd VARCHAR2(10), -- INFO/WARN/ERROR
message VARCHAR2(4000),
context CLOB
);
-- 監査ログAPIパッケージ
CREATE OR REPLACE PACKAGE pkg_audit AS
PROCEDURE info (p_module VARCHAR2, p_msg VARCHAR2, p_ctx CLOB DEFAULT NULL);
PROCEDURE warn (p_module VARCHAR2, p_msg VARCHAR2, p_ctx CLOB DEFAULT NULL);
PROCEDURE error(p_module VARCHAR2, p_msg VARCHAR2, p_ctx CLOB DEFAULT NULL);
END pkg_audit;
/
CREATE OR REPLACE PACKAGE BODY pkg_audit AS
-- 内部の汎用書き込み(独立トランザクション)
PROCEDURE write_log(
p_module VARCHAR2, p_level VARCHAR2,
p_msg VARCHAR2, p_ctx CLOB
) IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO audit_log(module, level_cd, message, context)
VALUES(p_module, p_level, SUBSTR(p_msg, 1, 4000), p_ctx);
COMMIT; -- 親に影響せず確定
EXCEPTION
WHEN OTHERS THEN
ROLLBACK; -- ログ書き込み失敗は親に伝えず黙殺
END;
PROCEDURE info(p_module VARCHAR2, p_msg VARCHAR2, p_ctx CLOB) IS
BEGIN write_log(p_module, 'INFO', p_msg, p_ctx); END;
PROCEDURE warn(p_module VARCHAR2, p_msg VARCHAR2, p_ctx CLOB) IS
BEGIN write_log(p_module, 'WARN', p_msg, p_ctx); END;
PROCEDURE error(p_module VARCHAR2, p_msg VARCHAR2, p_ctx CLOB) IS
BEGIN write_log(p_module, 'ERROR', p_msg, p_ctx); END;
END pkg_audit;
/
-- 利用例
BEGIN
-- 業務処理(途中で失敗)
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
pkg_audit.info('transfer', 'started');
-- 失敗発生
RAISE_APPLICATION_ERROR(-20100, 'simulated');
EXCEPTION
WHEN OTHERS THEN
pkg_audit.error('transfer', SQLERRM,
DBMS_UTILITY.FORMAT_ERROR_BACKTRACE);
ROLLBACK;
-- → 業務UPDATEは取消し、ログだけ残る
END;
/
ユースケース2|トリガーから監査記録を残す
テーブル更新時のトリガーで監査記録を残す古典的パターン。トリガー内のINSERTを独立化することで、親トランザクションがROLLBACKされても監査が残る設計になります(用途による・後述の注意点も確認)。
CREATE TABLE emp_audit( audit_id NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, emp_id NUMBER, old_sal NUMBER, new_sal NUMBER, who VARCHAR2(64), at_ts TIMESTAMP ); CREATE OR REPLACE TRIGGER trg_emp_salary_audit AFTER UPDATE OF salary ON employees FOR EACH ROW DECLARE PRAGMA AUTONOMOUS_TRANSACTION; BEGIN INSERT INTO emp_audit(emp_id, old_sal, new_sal, who, at_ts) VALUES(:OLD.id, :OLD.salary, :NEW.salary, USER, SYSTIMESTAMP); COMMIT; END; / -- 注意: 親のUPDATE EMPLOYEESがROLLBACKされても -- emp_auditには監査記録が残る(用途として正しいか業務要件を確認)
ユースケース3|リトライカウンタ/処理回数記録
失敗時のリトライカウンタを「失敗時にも必ず加算したい」要件。通常のUPDATEでは親のROLLBACKで一緒に戻ってしまうので、独立トランザクションで加算します。
CREATE TABLE retry_counter(
job_id VARCHAR2(64) PRIMARY KEY,
retry_cnt NUMBER DEFAULT 0 NOT NULL,
last_at TIMESTAMP
);
CREATE OR REPLACE PROCEDURE pkg_retry.bump(p_job_id VARCHAR2) IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
-- UPSERTで増分
MERGE INTO retry_counter t
USING (SELECT p_job_id AS job_id FROM dual) s
ON (t.job_id = s.job_id)
WHEN MATCHED THEN UPDATE SET
t.retry_cnt = t.retry_cnt + 1,
t.last_at = SYSTIMESTAMP
WHEN NOT MATCHED THEN INSERT(job_id, retry_cnt, last_at)
VALUES(p_job_id, 1, SYSTIMESTAMP);
COMMIT;
END;
/
-- 利用例:処理が失敗してもカウンタは確実に増える
BEGIN
pkg_retry.bump('JOB_ABC');
-- 業務処理
pkg_xxx.do();
COMMIT;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK; -- 業務はROLLBACK
RAISE; -- だがカウンタはCOMMIT済み
END;
/
ユースケース4|レート制限(API呼び出し回数の追跡)
外部APIの呼び出し回数を集計して、一定時間内のリクエスト数を制限する用途。業務処理が成功しても失敗しても呼び出した事実だけは記録したいので独立トランザクションが向きます。
CREATE TABLE api_rate_limit(
api_key VARCHAR2(64),
bucket_ts TIMESTAMP,
call_cnt NUMBER DEFAULT 0,
PRIMARY KEY(api_key, bucket_ts)
);
CREATE OR REPLACE FUNCTION pkg_rate.check_and_record(
p_api_key VARCHAR2,
p_limit NUMBER DEFAULT 100 -- 1分間に100回まで
) RETURN BOOLEAN AS
PRAGMA AUTONOMOUS_TRANSACTION;
v_bucket TIMESTAMP := TRUNC(SYSTIMESTAMP, 'MI');
v_cnt NUMBER;
BEGIN
-- 現在のバケットの件数を取得して+1
MERGE INTO api_rate_limit t
USING (SELECT p_api_key k, v_bucket b FROM dual) s
ON (t.api_key = s.k AND t.bucket_ts = s.b)
WHEN MATCHED THEN UPDATE SET t.call_cnt = t.call_cnt + 1
WHEN NOT MATCHED THEN INSERT(api_key, bucket_ts, call_cnt)
VALUES(s.k, s.b, 1);
SELECT call_cnt INTO v_cnt
FROM api_rate_limit
WHERE api_key = p_api_key AND bucket_ts = v_bucket;
COMMIT;
RETURN v_cnt <= p_limit;
END;
/
ユースケース5|キュー投入(イベントを必ず記録)
「処理が成功したか失敗したかにかかわらず、イベント発生の事実だけは外部キューに残す」監視・観測性向上の用途。実装は監査ログとほぼ同じパターンですが、本体トランザクションのCOMMIT後に処理を遅延させたい場合はAUTONOMOUS_TRANSACTIONよりDBMS_AQのような本物のキュー機能を使う方が適切な場合もあります。
CREATE TABLE event_queue(
event_id NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
occured_at TIMESTAMP DEFAULT SYSTIMESTAMP,
event_type VARCHAR2(64),
payload CLOB,
status VARCHAR2(10) DEFAULT 'PENDING'
);
CREATE OR REPLACE PROCEDURE pkg_event.publish(
p_type IN VARCHAR2, p_payload IN CLOB
) IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO event_queue(event_type, payload)
VALUES(p_type, p_payload);
COMMIT;
END;
/
-- 利用例:処理結果に関わらず必ずイベントを記録
BEGIN
pkg_event.publish('order.attempt',
'{"order_id": 100, "user_id": 50}');
-- 業務処理
pkg_order.create_order(100, 50);
COMMIT;
EXCEPTION
WHEN OTHERS THEN
pkg_event.publish('order.failed',
'{"order_id": 100, "error": "' || SQLERRM || '"}');
ROLLBACK;
RAISE;
END;
/
デッドロック対策|親と同じ表に触れる場合の罠
AUTONOMOUS_TRANSACTIONは独立した別トランザクションのため、親が保持しているロックは子から見ると他セッションが持っているロックと同じ扱いになります。結果として、親と子が同じ行や表をUPDATEすると自分自身でデッドロックを起こします。
-- ❌ デッドロック発生例
CREATE OR REPLACE PROCEDURE bad_logger(p_id NUMBER) IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
-- 親が更新中の同じ表を子も更新→ロック待ち
UPDATE orders SET status = 'LOG_RECORDED'
WHERE order_id = p_id;
COMMIT;
END;
/
DECLARE
BEGIN
UPDATE orders SET status = 'PROCESSING' WHERE order_id = 100;
bad_logger(100); -- ← ここで永久ロック待ち(タイムアウトでORA-00060)
COMMIT;
END;
/
-- ✅ 回避策1: 別の表に書く(推奨)
CREATE OR REPLACE PROCEDURE good_logger(p_id NUMBER, p_msg VARCHAR2) IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
-- 監査用の別テーブルに書く(親はordersをロック中だが
-- audit_logは独立しているので衝突なし)
INSERT INTO audit_log(module, message, context)
VALUES('orders', p_msg, TO_CHAR(p_id));
COMMIT;
END;
/
-- ✅ 回避策2: 同じ表を読むだけにする(書かない)
CREATE OR REPLACE PROCEDURE read_only_logger(p_id NUMBER) IS
PRAGMA AUTONOMOUS_TRANSACTION;
v_status VARCHAR2(20);
BEGIN
-- 読み取り専用ならロック衝突なし(READ COMMITTED)
-- ただし親の未COMMITの更新は見えない点に注意
SELECT status INTO v_status FROM orders WHERE order_id = p_id;
INSERT INTO audit_log(module, message)
VALUES('orders', 'pre-status: ' || v_status);
COMMIT;
END;
/
同一行への自己デッドロックは検出されない場合があるのが厄介です。通常のデッドロック(複数セッション間)はOracleが検出してORA-00060を返しますが、同一セッション内の親子間でロック待ちが発生するとそのままハングし、DDL_LOCK_TIMEOUT等のタイムアウトでようやくエラーになります。AUTONOMOUS_TRANSACTIONを書くときは「親は何のロックを持っているか」を常に意識し、同じ表への書き込みを避けてください。
ORA-06519対策|COMMIT/ROLLBACK忘れの典型エラー
ORA-06519: アクティブな自律トランザクションが検出されましたは、AUTONOMOUS_TRANSACTIONブロック内でCOMMITまたはROLLBACKを実行せずに終了した場合に発生します。基本的なミスですが、例外処理パスで漏れて見逃されることが多いエラーです。
-- ❌ ORA-06519が発生する例
CREATE OR REPLACE PROCEDURE leaky_log(p_msg VARCHAR2) IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO audit_log(message) VALUES(p_msg);
-- COMMIT忘れ!
END;
/
BEGIN leaky_log('test'); END;
-- ORA-06519: アクティブな自律トランザクションが検出されました
-- ✅ 防止策1: 必ず終端で明示的COMMIT
CREATE OR REPLACE PROCEDURE safe_log(p_msg VARCHAR2) IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO audit_log(message) VALUES(p_msg);
COMMIT;
END;
/
-- ✅ 防止策2: 例外時もROLLBACKを必ず書く
CREATE OR REPLACE PROCEDURE robust_log(p_msg VARCHAR2) IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO audit_log(message) VALUES(p_msg);
COMMIT;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK; -- 例外時もきちんと閉じる
-- ログAPI内の失敗は親に伝えない(黙殺)
END;
/
-- ✅ 防止策3: 標準テンプレートに沿って書く
CREATE OR REPLACE PROCEDURE template_log(p_msg VARCHAR2) IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
-- 業務処理
INSERT INTO audit_log(message) VALUES(p_msg);
COMMIT; -- 必須:成功パス
EXCEPTION
WHEN OTHERS THEN
ROLLBACK; -- 必須:失敗パス
NULL; -- ログ失敗は親に伝えない/RAISEするなら明示
END;
/
テンプレート徹底が最強の防止策です。①関数の最後に必ずCOMMIT、②EXCEPTION句で必ずROLLBACK、の2点をテンプレ化してコードレビューでチェック対象にしてください。社内のSnippetやIDEテンプレートに登録しておくと新規実装時の漏れがゼロになります。
SAVEPOINT との使い分け|決定木で判断する
「親トランザクションに影響を与えたくない」要件でSAVEPOINTとAUTONOMOUS_TRANSACTIONはよく混同されますが、決定的に役割が異なります。次の判断フローで明確に分けてください。
判断フローチャート
「親と独立した別トランザクションが必要か?」を最初に問います。
- YES(親が失敗しても残したい記録)→ AUTONOMOUS_TRANSACTION
- NO(親と同じトランザクション内で完結)→ 次の質問へ
NOの場合さらに:「途中状態を部分的に取り消したいか?」
- YES(ステップ単位で巻き戻したい)→ SAVEPOINT
- NO(成功か失敗かの2択でよい)→ 普通のCOMMIT/ROLLBACKのみ
具体例での判断
- 「失敗時もエラーログだけは残したい」→ AUTONOMOUS_TRANSACTION
- 「失敗行だけスキップして他の行は処理したい」→ SAVEPOINT
- 「リトライ回数を必ず数えたい」→ AUTONOMOUS_TRANSACTION
- 「ステップ2が失敗したらステップ1まで戻したい」→ SAVEPOINT
- 「外部APIの呼出し回数を独立に集計したい」→ AUTONOMOUS_TRANSACTION
- 「複数ジョブの失敗を吸収して成功した分だけ残したい」→ SAVEPOINT
SAVEPOINTとの併用は可能です。監査ログAPIをAUTONOMOUS_TRANSACTIONで実装し、業務処理側ではSAVEPOINTで段階的ロールバックする、という組み合わせが本格的なアプリケーションでは標準的。SAVEPOINTの詳細はSAVEPOINTで部分ロールバックを実装する完全ガイドを参照してください。
テスト方法|独立トランザクションを単体テストする
AUTONOMOUS_TRANSACTIONは独立コミットするため、テスト後のクリーンアップが厄介になります。通常の単体テストは「テスト前後で状態が変わらないようにROLLBACKで戻す」前提で書きますが、自律トランザクションはROLLBACKでは戻りません。対処パターンを整理します。
-- パターン1: テスト前後で対象表をクリーンアップ
CREATE OR REPLACE PROCEDURE test_audit_log AS
v_cnt NUMBER;
BEGIN
-- 準備:監査表をクリア
DELETE FROM audit_log WHERE module = 'TEST_MODULE';
COMMIT;
-- テスト実行
pkg_audit.info('TEST_MODULE', 'test message');
-- 検証
SELECT COUNT(*) INTO v_cnt
FROM audit_log
WHERE module = 'TEST_MODULE' AND level_cd = 'INFO';
IF v_cnt != 1 THEN
RAISE_APPLICATION_ERROR(-20999, 'audit log not recorded');
END IF;
-- 後始末:テストデータ削除
DELETE FROM audit_log WHERE module = 'TEST_MODULE';
COMMIT;
END;
/
-- パターン2: 専用テストスキーマ/テスト用接尾辞で隔離
-- AUTONOMOUS関数を本物のログ表ではなく
-- _TEST 接尾辞のテーブルに向ける設定で実行
-- → 設定値はSYS_CONTEXTやパッケージ定数で切替
-- パターン3: utPLSQLのbefore/afterでクリーンアップ
-- @BeforeEach: テスト前にDELETE+COMMITで初期状態へ
-- @AfterEach: テスト後にDELETE+COMMITで後始末
utPLSQLフレームワークを使う場合、--%beforeeachと--%aftereachにクリーンアップ処理を書くのが定石です。CIで自動実行するときは必ず専用のテストスキーマで走らせ、本番データを汚染しないようにしてください。
本番で踏むアンチパターン6選
① 業務データの更新に使う
「親が失敗しても在庫を減らしたい」のような業務要件でAUTONOMOUS_TRANSACTIONを使うと、整合性が完全に壊れます。在庫だけ減って注文が消える状態になり修正不能。業務データは必ず親と同じトランザクション内で扱ってください。
② COMMIT/ROLLBACKを書き忘れてORA-06519
ありがちな単純ミスですが、例外パスでだけ漏れて本番に出てしまうケースが頻発します。EXCEPTION句で必ずROLLBACKまたはCOMMITを書くテンプレートを徹底してください。
③ 親と同じ表を更新してデッドロック
独立トランザクションだから別セッション扱いになる、と理解せず同じ表を更新してハング。「同じ表は触らない」を原則とし、必要なら別表または読み取り専用にしてください。
④ 監査ログAPIを各箇所で個別実装
毎回PRAGMA宣言して個別にINSERT+COMMITを書く実装が散在すると、COMMIT忘れ・PRAGMA忘れが各所で発生します。必ず1本のパッケージに集約して全員がそれを呼ぶ規約に。
⑤ 例外を握りつぶして失敗を隠す
監査ログ書き込み自体が失敗した場合、親に伝えるべきか・黙殺すべきかの判断は重要です。一般論として「ログ機構の失敗で業務が止まるのは本末転倒」なので黙殺+ROLLBACKが正解ですが、セキュリティ要件で監査が必須な場合は親に伝搬させる必要があります。要件に応じて使い分けてください。
⑥ ネスト呼び出しで意図せず多重コミット
AUTONOMOUS関数の中からさらにAUTONOMOUS関数を呼ぶことは可能ですが、それぞれが独立してCOMMITするため、「全部成功してから書く」ような原子性は得られません。ネストする設計はできるだけ避け、必要なら親の単一AUTONOMOUS関数でまとめて実装してください。
よくある質問
COMMITやROLLBACKが実行できません(ORA-06519が発生)。AUTONOMOUS_TRANSACTIONを宣言すると、そのトリガーを独立トランザクションとして扱えるためCOMMITが書けるようになります。主に監査トリガーで使われますが、「業務テーブルの更新がROLLBACKされても監査だけ残す」用途は業務要件として正しいかを慎重に検討してください。関連記事で深掘りする
AUTONOMOUS_TRANSACTIONに関連する周辺技術もあわせて押さえましょう。
- 【Oracle】PRAGMA AUTONOMOUS_TRANSACTION完全ガイド|独立トランザクションで監査ログ・エラーログを確実に残す方法(機能網羅と基本構文)
- 【PL/SQL】SAVEPOINTで部分ロールバックを実装する完全ガイド(同一TX内の部分制御)
- 【PL/SQL】COMMITとROLLBACKの正しい使い方(トランザクション境界の基本)
- 【Oracle】トランザクション完全ガイド(COMMIT/ROLLBACK/SAVEPOINT/分離レベル全般)
- 【PL/SQL】例外処理完全ガイド(EXCEPTION句との統合)
- 【PL/SQL】MERGE文でUPSERTを高速・安全に実装(リトライカウンタのMERGE実装)
- 【PL/SQL】パッケージ設計でコード管理と再利用性を極める(監査ログAPIのパッケージ化)
- 【PL/SQL】トリガー完全ガイド(監査トリガーの実装パターン)
- 【PL/SQL】DBMS_SCHEDULERでジョブ管理を極める(ジョブ内のログ書き込みでAUTONOMOUSを活用)
- 【PL/SQL】コンパイル時エラーと警告の完全対処ガイド(USER_ERRORSへの書き込み等の応用)
まとめ|AUTONOMOUS_TRANSACTIONを正しく使い切る
AUTONOMOUS_TRANSACTIONは「親と独立した別トランザクション」を実現する強力な仕組みで、監査ログ・エラー記録・カウンタなど「親が失敗しても残したい記録」の実装に欠かせません。ただし業務データに使うと整合性が壊れ、同じ表に触れるとデッドロック、COMMIT忘れでORA-06519と事故源も多い機能です。本記事の要点を7つに集約します。
- 用途は監査ログ・エラー記録・カウンタ系に限定し、業務データには使わない
- 必ずCOMMITまたはROLLBACKで終わらせ、ORA-06519を防ぐ
- 親と同じ表を更新するとデッドロックする。原則は別表に書く
- 監査ログはパッケージ化して集中管理、PRAGMAは内部関数1本だけ
- SAVEPOINTとは別物。「親と独立か同一か」で機能を選ぶ
- テストは前後でクリーンアップが必要。utPLSQLのhookを活用
- JDBCのrollbackには影響されず、独立コミットの内容は確実に残る
レガシーコードで「失敗時にエラーログがROLLBACKされて記録が残らない」事故が起きていれば、共通ログAPIをAUTONOMOUS_TRANSACTIONで実装し直すだけで運用が劇的に改善します。本記事のユースケース5種を実装テンプレとして自プロジェクトに適用してみてください。

