[ PHP ] OCI8 による Oracle 接続でトランザクションの混信を防止する

Pocket

ここでは、OCI8 による oracle 接続でのトランザクションの混信について掲載しています。

スポンサーリンク

OCI8 で Oracle に接続する関数は、以下の 3種類があります。

関数名 説明
oci_connect Oracle データベースに接続する
oci_pconnect 持続的接続を使用してOracle データベースに接続する
oci_new_connect 一意な接続を使って Oracle サーバへ接続する

リンク先にも記述があるように、oci_connect と oci_pconnect 関数は、Oracle への接続が共有されます。oci_pconnect では、HTTP リクエスト間まで範囲を広げた共有が行われます。

~ リンクの説明文より引用 ~

典型的な PHP アプリケーションでは、Apache の子プロセス (もしくは PHP FastCGI/CGI プロセス) ごとに Oracle サーバに対してオープンされた単一の持続的接続を有します。)

持続的な接続とは

持続的な接続については、言葉による説明よりもサンプルコードを確認したほうが理解が早いと思いますので、以下のサンプルをご確認ください。詳細はコメントを確認してください。

$conn1 = oci_connect('user', 'password', 'localhost/XE');
$conn2 = oci_connect('user', 'password', 'localhost/XE');

// $conn1 も $conn2 も同一の内容を出力する
var_dump($conn1);
var_dump($conn2);

// 上記は以下のコードと等価
$conn1 = oci_connect('user', 'password', 'localhost/XE');
$conn2 = &$conn1;
トランザクションが混信する不具合サンプルコード

上記のことから、2つのコネクションでそれぞれトランザクション制御を行うと、以下のような不具合が発生します。詳細はサンプル内のコメントを参照ください。

// oracle へ接続(同一の接続子を返す)
$conn1 = oci_connect('user', 'password', 'localhost/XE');
$conn2 = oci_connect('user', 'password', 'localhost/XE');

// $conn1 に対してレコードを挿入する
$stid1 = oci_parse($conn1, 'insert into t01 (f01, f02) values (:val1, :val2)');
$col1 = "value1";
$col2 = "value2";
oci_bind_by_name($stid1, ':val1', $col1);
oci_bind_by_name($stid1, ':val2', $col2);
oci_execute($stid1, OCI_NO_AUTO_COMMIT);

// $conn2 に対してレコードを挿入する
$stid2 = oci_parse($conn2, 'insert into t01 (f01, f02) values (:val1, :val2)');
$col1 = "value3";
$col2 = "value4";
oci_bind_by_name($stid2, ':val1', $col1);
oci_bind_by_name($stid2, ':val2', $col2);
oci_execute($stid2, OCI_NO_AUTO_COMMIT);

// $conn1 をコミットする
oci_commit($conn1);

/*
 $conn2 をロールバックする
 しかし、すでに $conn1 でコミットされているため $conn2 に挿入した
 レコードはデータベースに挿入されてしまっている状態となる
*/
oci_rollback($conn2);

oci_free_statement($stid1);
oci_free_statement($stid2);

oci_close($conn1);
oci_close($conn2);
独立したコネクション管理

複数のコネクションに対して同時にそれぞれのトランザクション管理を行う必要がある場合は、 以下のように、oci_new_connect 関数を使用してそれぞれのコネクションを作成します。

$conn1 = oci_connect('user', 'password', 'localhost/XE');
// 新規に独立した接続を行う
$conn2 = oci_new_connect('user', 'password', 'localhost/XE');

// $conn1 と $conn2 は異なる内容を出力する
var_dump($conn1);
var_dump($conn2);
スポンサーリンク


Pocket

Leave a Comment

Your email address will not be published. Required fields are marked *