<?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 => 'ru',
  ),
  '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' => 'ru',
    '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 and 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">
   К таким примерам относится попытка запуска транзакций в таблицах
   MyISAM базы данных MySQL.
  </p>
 </div>
 <div class="warning"><strong class="warning">Внимание</strong>
  <p class="para">
   <em>Неявные фиксации при выполнении DDL-запросов:</em>
   Отдельные базы данных неявно выполняют команду <code class="literal">COMMIT</code>
   и фиксируют изменения в одной транзакции при выполнении серии DDL-запросов (англ. Database Definition Language)
   наподобие <code class="literal">DROP TABLE</code> или <code class="literal">CREATE TABLE</code>.
   Поэтому изменения, которые оказались в транзакции,
   <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 /><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">// Неявная фиксация командой COMMIT выполняется в этом месте<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">// Невозможно отменить серию команд INSERT/CREATE в БД MySQL или Oracle<br /><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>
    В следующем примере предположим, что требуется создать набор записей для нового
    сотрудника с идентификатором 23. Кроме ввода базовой информации о сотруднике потребуется
    записать его зарплату. Вместо двух отдельных изменений
    обернём обновление таблиц методами
    <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 /><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">(<br />        </span><span style="color: #DD0000">'odbc:SAMPLE'</span><span style="color: #007700">,<br />        </span><span style="color: #DD0000">'db2inst1'</span><span style="color: #007700">,<br />        </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 />    );<br /><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) 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 />} 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 /><br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
   </div>

  </div>
 </p>
 <p class="para">
  Транзакции не ограничивают разработчика только изменением данных; в транзакции заворачивают
  сложные запросы для извлечения данных, на основе которых
  создают больше запросов на обновление и другие запросы; активная
  транзакция гарантирует, что никто не изменит данные,
  пока идёт работа с транзакцией. Дополнительную информацию о транзакциях
  содержит документация к серверу баз данных.
 </p>
</div>
<?php manual_footer($setup); ?>