<?php
include_once $_SERVER['DOCUMENT_ROOT'] . '/include/shared-manual.inc';
$TOC = array();
$TOC_DEPRECATED = array();
$PARENTS = array();
include_once dirname(__FILE__) ."/toc/book.pdo.inc";
$setup = array (
  'home' => 
  array (
    0 => 'index.php',
    1 => 'PHP Manual',
  ),
  'head' => 
  array (
    0 => 'UTF-8',
    1 => 'ja',
  ),
  'this' => 
  array (
    0 => 'pdo.transactions.php',
    1 => 'トランザクションおよび自動コミット',
    2 => 'トランザクションおよび自動コミット',
  ),
  'up' => 
  array (
    0 => 'book.pdo.php',
    1 => 'PDO',
  ),
  'prev' => 
  array (
    0 => 'pdo.connections.php',
    1 => '接続、および接続の管理',
  ),
  'next' => 
  array (
    0 => 'pdo.prepared-statements.php',
    1 => 'プリペアドステートメントおよびストアドプロシージャ',
  ),
  'alternatives' => 
  array (
  ),
  'source' => 
  array (
    'lang' => 'ja',
    'path' => 'reference/pdo/transactions.xml',
  ),
  'history' => 
  array (
  ),
);
$setup["toc"] = $TOC;
$setup["toc_deprecated"] = $TOC_DEPRECATED;
$setup["parents"] = $PARENTS;
manual_setup($setup);

contributors($setup);

?>
<div id="pdo.transactions" class="chapter">
 <h1 class="title">トランザクションおよび自動コミット</h1>

 <p class="para">
  さあ、PDO を使用してデータベースに接続することができました。
  クエリを発行する前に、PDO がトランザクションをどのように扱うのかを
  理解しなければなりません。トランザクションについてよくわからない方の
  ために説明すると、これは以下の 4 つの機能、つまり
  原子性 - Atomicity、一貫性 - Consistency、独立性 - Isolation および
  永続性 - Durability (ACID) を提供するものです。
  一般的な言葉で言うと、トランザクション内で実行された作業は
  たとえ段階的に行われたものであってもデータベースに安全に反映される
  ことが保証されています。トランザクションのコミット時に他の接続の
  干渉を受けることはありません。また、トランザクション内での作業は
  (まだコミットされていなければ) いつでも自動的に取り消すことが
  できます。これにより、スクリプト内でのエラー処理がより楽になります。
 </p>
 <p class="para">
  トランザクションの一般的な実装は、変更内容を一時的に「蓄えて」おき、
  それを一気に適用するようになっています。この実装には、更新処理の
  性能を劇的に向上させるという効果もあります。つまり、
  トランザクションによってあなたの書くスクリプトはより高速になり、
  また、より堅牢になるということです
  (これらの恩恵をうけるためには、トランザクションを正しく使用する
  必要があります)。
 </p>
 <p class="para">
  残念ながら、すべてのデータベースがトランザクションをサポートしていると
  いうわけではありません。そのため、PDO で最初に接続をオープンした際には、
  いわゆる「自動コミット」モードで動作します。自動コミットモードとは、
  もしデータベースがトランザクションをサポートしていたら個々のクエリが
  暗黙的なトランザクションのもとで実行され、サポートしていなかったら
  トランザクションを使用せずに実行されることを意味します。
  トランザクションを使用する場合は、
  <span class="methodname"><a href="pdo.begintransaction.php" class="methodname">PDO::beginTransaction()</a></span> メソッドを使用して
  トランザクションを初期化する必要があります。使用しているドライバが
  トランザクションをサポートしていない場合は PDOException が
  スローされます (これは深刻な状態であるため、エラー処理の設定に
  かかわらず常にスローされます)。初期化した後でトランザクションを
  終了させるには、トランザクション内でのコードが成功したか否かに応じて
  <span class="methodname"><a href="pdo.commit.php" class="methodname">PDO::commit()</a></span> あるいは
  <span class="methodname"><a href="pdo.rollback.php" class="methodname">PDO::rollBack()</a></span> を使用します。
 </p>
 <div class="warning"><strong class="warning">警告</strong>
  <p class="para">
   PDO は、トランザクション機能をドライバレベルでしかチェックしません。
   実行時に何らかの条件でトランザクションが使えない場合でも、
   データベースサーバーがトランザクション開始要求を受け付けた場合には
   <span class="methodname"><a href="pdo.begintransaction.php" class="methodname">PDO::beginTransaction()</a></span> は <strong><code><a href="reserved.constants.php#constant.true">true</a></code></strong>
   を返します。エラーとはなりません。
  </p>
  <p class="para">
   たとえば、MySQL データベースの
   MyISAM テーブルでトランザクションを使おうとした場合にこの現象が発生します。
  </p>
 </div>
 <div class="warning"><strong class="warning">警告</strong>
  <p class="para">
   <em>DDL文を使った場合の暗黙のコミット:</em>
   データベースによっては、
   <code class="literal">DROP TABLE</code> や <code class="literal">CREATE TABLE</code>,
   のような database definition language (DDL)
   がトランザクション中に実行された場合に、
   自動で暗黙のうちに <code class="literal">COMMIT</code> を発行するものがあります。
   これは、DDL 文より前に行われた、同じトランザクション中のあらうる変更が
   <em>自動でコミット</em> さて、ロールバックできないことを意味します。
  </p>
  <p class="para">
   こうした振る舞いをするデータベースの例として、
   <code class="literal">MySQL</code> と <code class="literal">Oracle</code>
   が挙げられます。
  </p>
 </div>
 <p class="para">
  <div class="example" id="example-1">
   <p><strong>例1 暗黙のコミットの例</strong></p>
   <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br />$pdo</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">beginTransaction</span><span style="color: #007700">();<br /></span><span style="color: #0000BB">$pdo</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">exec</span><span style="color: #007700">(</span><span style="color: #DD0000">"INSERT INTO users (name) VALUES ('Rasmus')"</span><span style="color: #007700">);<br /></span><span style="color: #0000BB">$pdo</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">exec</span><span style="color: #007700">(</span><span style="color: #DD0000">"CREATE TABLE test (id INT PRIMARY KEY)"</span><span style="color: #007700">); </span><span style="color: #FF8000">// Implicit COMMIT occurs here<br /></span><span style="color: #0000BB">$pdo</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">rollBack</span><span style="color: #007700">(); </span><span style="color: #FF8000">// This will NOT undo the INSERT/CREATE for MySQL or Oracle<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
   </div>

  </div>
 </p>
 <p class="para">
  <em>ベストプラクティス:</em> このような振る舞いを強制してくるデータベースを使う場合は、トランザクション中で DDL を使うのを避けましょう。
  必要な場合には、DDL の操作をトランザクションのロジックと分離しましょう。
 </p>
 <p class="para">
  スクリプトが終了したり接続が閉じられようとした際に、もし処理が
  完了していないトランザクションがあれば PDO が自動的に
  ロールバックします。これは、スクリプトが予期せぬ状態で終了した場合に
  データの不整合が発生するのを避けるための安全装置です。もし
  明示的にコミットしていなければ、おそらく何かおかしなことが
  起こったのだろうと推測されます。そのため、データを守るために
  ロールバックが行われるのです。
 </p>
 <div class="warning"><strong class="warning">警告</strong>
  <p class="para">
   自動的にロールバックが行われるのは、トランザクションを
   <span class="methodname"><a href="pdo.begintransaction.php" class="methodname">PDO::beginTransaction()</a></span> で開始した場合のみです。
   トランザクションを開始するクエリを手動で発行した場合、
   PDO はそれを知ることができません。そのため、何か問題が発生しても
   ロールバックすることはできないのです。
  </p>
 </div>
 <p class="para">
  <div class="example" id="example-2"><p><strong>例2 トランザクション内で一括処理を行う</strong></p>
   <div class="example-contents"><p>
    以下の例では、新しい従業員のデータを作成しているものとします。
    この従業員には ID 番号 23 を割り当てます。この人物についての
    基礎データを入力するだけでなく、その給与についても登録する
    必要があります。以下では単純に 2 つの別々の更新を行っていますが、
    それらは <span class="methodname"><a href="pdo.begintransaction.php" class="methodname">PDO::beginTransaction()</a></span> および
    <span class="methodname"><a href="pdo.commit.php" class="methodname">PDO::commit()</a></span> のコールで囲まれています。
    これにより、変更が完了するまでは他からは一切変更内容が
    見えないことが保証されます。もし何か問題が発生すれば、
    catch ブロック内でトランザクション開始以降のすべての変更が
    ロールバックされます。そしてエラーメッセージを表示します。
   </p></div>
   <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /></span><span style="color: #007700">try {<br />  </span><span style="color: #0000BB">$dbh </span><span style="color: #007700">= new </span><span style="color: #0000BB">PDO</span><span style="color: #007700">(</span><span style="color: #DD0000">'odbc:SAMPLE'</span><span style="color: #007700">, </span><span style="color: #DD0000">'db2inst1'</span><span style="color: #007700">, </span><span style="color: #DD0000">'ibmdb2'</span><span style="color: #007700">, <br />      array(</span><span style="color: #0000BB">PDO</span><span style="color: #007700">::</span><span style="color: #0000BB">ATTR_PERSISTENT </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">true</span><span style="color: #007700">));<br />  echo </span><span style="color: #DD0000">"接続しました\n"</span><span style="color: #007700">;<br />} catch (</span><span style="color: #0000BB">Exception $e</span><span style="color: #007700">) {<br />  die(</span><span style="color: #DD0000">"接続できません: " </span><span style="color: #007700">. </span><span style="color: #0000BB">$e</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">getMessage</span><span style="color: #007700">());<br />}<br /><br />try {  <br />  </span><span style="color: #0000BB">$dbh</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">setAttribute</span><span style="color: #007700">(</span><span style="color: #0000BB">PDO</span><span style="color: #007700">::</span><span style="color: #0000BB">ATTR_ERRMODE</span><span style="color: #007700">, </span><span style="color: #0000BB">PDO</span><span style="color: #007700">::</span><span style="color: #0000BB">ERRMODE_EXCEPTION</span><span style="color: #007700">);<br /><br />  </span><span style="color: #0000BB">$dbh</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">beginTransaction</span><span style="color: #007700">();<br />  </span><span style="color: #0000BB">$dbh</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">exec</span><span style="color: #007700">(</span><span style="color: #DD0000">"insert into staff (id, first, last) values (23, 'Joe', 'Bloggs')"</span><span style="color: #007700">);<br />  </span><span style="color: #0000BB">$dbh</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">exec</span><span style="color: #007700">(</span><span style="color: #DD0000">"insert into salarychange (id, amount, changedate) <br />      values (23, 50000, NOW())"</span><span style="color: #007700">);<br />  </span><span style="color: #0000BB">$dbh</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">commit</span><span style="color: #007700">();<br />  <br />} catch (</span><span style="color: #0000BB">Exception $e</span><span style="color: #007700">) {<br />  </span><span style="color: #0000BB">$dbh</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">rollBack</span><span style="color: #007700">();<br />  echo </span><span style="color: #DD0000">"失敗しました。" </span><span style="color: #007700">. </span><span style="color: #0000BB">$e</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">getMessage</span><span style="color: #007700">();<br />}<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
   </div>

  </div>
 </p>
 <p class="para">
  トランザクション内で行える処理は、更新には限りません。たとえば
  何か複雑なクエリを発行してデータを抽出し、その情報をもとに
  データの更新をしたり別のクエリを実行したりすることも可能です。
  トランザクションがアクティブな間は、作業中のデータについては
  他から一切変更が加えられないことが保証されます。
  トランザクションについての詳細は、
  使用するデータベースサーバーのドキュメントを参照ください。
 </p>
</div>
<?php manual_footer($setup); ?>