PDO::beginTransaction

(PHP 5 >= 5.1.0, PECL pdo >= 0.1.0)

PDO::beginTransaction Инициализация транзакции

Описание

bool PDO::beginTransaction ( void )

Выключает режим автоматической фиксации транзакции. В то время, как режим автоматической фиксации выключен, изменения, внесенные в базу данных через объект экземпляра PDO, не применяются, пока вы не завершите транзакцию, вызвав PDO::commit(). Вызов PDO::rollBack() откатит все изменения в базе данных и вернет соединение к режиму автоматической фиксации.

Некоторые базы данных, включая MySQL, автоматически выполняют неявную фиксацию, когда выражения языка описания данных (DDL), такие как DROP TABLE или CREATE TABLE, находятся внутри транзакции. Неявная фиксация предотвратит вам откат любых изменений в рамках границ транзакции.

Возвращаемые значения

Возвращает TRUE в случае успешного завершения или FALSE в случае возникновения ошибки.

Примеры

Пример #1 Откат транзакции

Следующий пример начинает транзацию и выполняет два заявления, что изменяет базу данных до отката изменений. В MySQL, тем не менее, выражение DROP TABLE автоматически фиксирует транзакцию так, что ни одно из изменений в транзакции не откатывается назад.

<?php
/* Начало транзакции, отключение автоматической фиксации */
$dbh->beginTransaction();

/* Изменение схемы базы данных и данных */
$sth $dbh->exec("DROP TABLE fruit");
$sth $dbh->exec("UPDATE dessert
    SET name = 'hamburger'"
);

/* Распознаем ошибку и откатываем назад изменения */
$dbh->rollBack();

/* Соединение с базой данных снова в режиме автоматической фиксации */
?>

Смотрите также

Коментарии

Автор:
beginTransaction will through a PDOException if you execute it while a PDO transaction is already active.  Additionally the PDO engine doesn't seem to provide any way of determining if there is a transaction "in flight" so if you might be calling a function from within another function that starts a transaction you'll have to wrap the beginTransaction () call in a try .. catch block.
2007-12-20 09:04:56
http://php5.kiev.ua/manual/ru/pdo.begintransaction.html
Автор:
In response to "Anonymous / 20-Dec-2007 03:04"

You could also extend the PDO class and hold a private flag to check if a transaction is already started.

class MyPDO extends PDO {
   protected $hasActiveTransaction = false;

   function beginTransaction () {
      if ( $this->hasActiveTransaction ) {
         return false;
      } else {
         $this->hasActiveTransaction = parent::beginTransaction ();
         return $this->hasActiveTransaction;
      }
   }

   function commit () {
      parent::commit ();
      $this->hasActiveTransaction = false;
   }

   function rollback () {
      parent::rollback ();
      $this->hasActiveTransaction = false;
   }

}
2008-02-11 08:37:56
http://php5.kiev.ua/manual/ru/pdo.begintransaction.html
// If you need to set an ISOLATION level or LOCK MODE it needs to be done BEFORE you make the BeginTransaction() call...
//
//  **note** you should always check result codes on operations and do error handling.  This sample code
//  assumes all the calls work so that the order of operations is accurate and easy to see
//
//  THIS IS using the PECL PDO::INFORMIX module, running on fedora core 6, php 5.2.4
//
//    This is the correct way to address an informix -243 error (could not position within table) when there
//    is no ISAM error indicating a table corruption.  A -243 can happen (if the table/indexes, etc., are ok) 
//    if a row is locked.  The code below sets the LOCK MODE to wait 2 minutes (120 seconds) before
//    giving up.  In this example you get READ COMMITTED rows, if you don't need read committed
//    but just need to get whatever data is there (ignoring locked rows, etc.) instead of
//    "SET LOCK MODE TO WAIT 120" you could "SET ISOLATION TO DIRTY READ".
//
//    In informix you *must* manage how you do reads because it is very easy to trigger a
//    lock table overflow (which downs the instance) if you have lots of rows, are using joins
//    and have many updates happening. 
//

// e.g.,

$sql= "SELECT FIRST 50 * FROM mytable WHERE mystuff=1 ORDER BY myid";                    /* define SQL query */

try                                                                                /* create an exception handler */
    {
    $dbh = new PDO("informix:host=......");
         
    if ($dbh)    /* did we connect? */
        {
        $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        $dbh->query("SET LOCK MODE TO WAIT 120")
       
        # ----------------
        # open transaction cursor
        # ----------------
        if    ( $dbh->beginTransaction() )                                         # explicitly open cursor
            {
            try    /* open exception handler */
                {
                $stmt = $dbh->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL));

                $stmt->execute();
               
                while ($row = $stmt->fetch(PDO::FETCH_NUM, PDO::FETCH_ORI_NEXT))
                    {
                    $data = $row[0] . "\t" . $row[1] . "\t" . $row[2] . "\t" . $row[3] . "\t" . $row[4] . "\t" . $row[5] . "\t" . $row[6] . "\t" . $row[7] . "\n" . $row[8] ;
                    //print $data;
                    print_r($row);
                    };
               
                $stmt = null;
                }
            catch (PDOException $e)
                {
                print "Query Failed!\n\n";
               
                print "DBA FAIL:" . $e->getMessage();
                };
           
            $dbh->rollback();                                                       # abort any changes (ie. $dbh->commit()
            $dbh = null;                                                            # close connection
            }
        else
            {
            # we should never get here, it should go to the exception handler
            print "Unable to establish connection...\n\n";
            };
        };
    }
catch (Exception $e)
    {
    $dbh->rollback();
    echo "Failed: " . $e->getMessage();
    };
2008-08-20 12:21:42
http://php5.kiev.ua/manual/ru/pdo.begintransaction.html
If you are using PDO::SQLITE and need to support a high level of concurrency with locking, try preparing your statements prior to calling beginTransaction() and you may also need to call closeCursor() on SELECT statements to prevent the driver from thinking that there are open transactions.

Here's an example (Windows, PHP version 5.2.8).  We test this by opening 2 browser tabs to this script and running them at the same time.  If we put the beginTransaction before the prepare, the second browser tab would hit the catch block and the commit would throw another PDOException indicating that transactions were still open.

<?php
$conn 
= new PDO('sqlite:C:\path\to\file.sqlite');
$stmt $conn->prepare('INSERT INTO my_table(my_id, my_value) VALUES(?, ?)');
$waiting true// Set a loop condition to test for
while($waiting) {
    try {
       
$conn->beginTransaction();
        for(
$i=0$i 10$i++) {
           
$stmt->bindValue(1$iPDO::PARAM_INT);
           
$stmt->bindValue(2'TEST'PDO::PARAM_STR);
           
$stmt->execute();
           
sleep(1);
        }
       
$conn->commit();
       
$waiting false;
    } catch(
PDOException $e) {
        if(
stripos($e->getMessage(), 'DATABASE IS LOCKED') !== false) {
           
// This should be specific to SQLite, sleep for 0.25 seconds
            // and try again.  We do have to commit the open transaction first though
           
$conn->commit();
           
usleep(250000);
        } else {
           
$conn->rollBack();
            throw 
$e;
        }
    }
}

?>
2009-04-11 17:27:47
http://php5.kiev.ua/manual/ru/pdo.begintransaction.html
With Oracle, any structure statement will do an implicit commit.

So : ALTER TABLE "my_table" DROP COLUMN "my_column";
Can't be rolled back !

Hope this will save time for others
2009-12-28 15:58:10
http://php5.kiev.ua/manual/ru/pdo.begintransaction.html
be aware that you also can not use TRUNCATE TABLE as this statement will trigger a commit just like CREATE TABLE or DROP TABLE

it is best to only use SELECT, UPDATE and DELETE within a transaction, all other statements may cause commits thus breaking the atomicity of your transactions and their ability to rollback

obviously you can use DELETE FROM <table> instead of TRUNCATE TABLE but be aware that there are differences between both statements, for example TRUNCATE resets the auto_increment value while DELETE does not.
2010-06-08 12:23:03
http://php5.kiev.ua/manual/ru/pdo.begintransaction.html
Автор:
You can generate problems with nested beginTransaction and commit calls.
example:

beginTransaction()
do imprortant stuff
call method
    beginTransaction()
    basic stuff 1
    basic stuff 2
    commit()
do most important stuff
commit()

Won't work and is dangerous since you could close your transaction too early with the nested commit().

There is no need to mess you code and pass like a bool which indicate if transaction is already running. You could just overload the beginTransaction() and commit() in your PDO wrapper like this:

<?php
class Database extends \\PDO
{
    protected 
$transactionCounter 0;
    function 
beginTransaction()
    {
        if(!
$this->transactionCounter++)
            return 
parent::beginTransaction();
       return 
$this->transactionCounter >= 0;
    }

    function 
commit()
    {
       if(!--
$this->transactionCounter)
           return 
parent::commit();
       return 
$this->transactionCounter >= 0;
    }

    function 
rollback()
    {
        if(
$this->transactionCounter >= 0)
        {
           
$this->transactionCounter 0;
            return 
parent::rollback();
        }
       
$this->transactionCounter 0;
        return 
false;
    }
//...
}
?>
2012-08-17 00:27:14
http://php5.kiev.ua/manual/ru/pdo.begintransaction.html
For the PDO driver for the  Firebird server, you have to explicitly disable autocommit to initiate the  transaction as well as explicitly enable autocommit to commit it.

$dbh->setAttribute( PDO::ATTR_AUTOCOMMIT, 0 );
$dbh->beginTransaction();

/** Query block */

$dbh->commit();
$dbh->setAttribute( PDO::ATTR_AUTOCOMMIT, 1 );
2013-11-04 01:22:59
http://php5.kiev.ua/manual/ru/pdo.begintransaction.html
Strange behavior with pdo_firebird driver:

<?php
$dbh 
= new PDO ('','','',array( PDO :: ATTR_PERSISTENT=>true));
$dbh->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION ); 
$dbh -> exec "select 1;" ); //should be autocommitted
try

   
//$dbh->commit/rollback here fixes the issue;
   
$dbh -> beginTransaction (); // <- fails "with there is already an active transaction"
   
$dbh -> exec "select 1;" ); 
   
$dbh -> commit (); 
}
catch ( 
Exception $e )

 
$dbh -> rollBack (); 
 } 
?>
2014-04-07 11:53:19
http://php5.kiev.ua/manual/ru/pdo.begintransaction.html
Автор:
after TRUNCATE TABLE `table`  just as DELETE FROM `table`, so if whole table was deleted, aborts the transaction. And the rollback will not be passible.
2014-11-28 23:16:40
http://php5.kiev.ua/manual/ru/pdo.begintransaction.html
Автор:
The example is misleading, Typically data definition language clauses (DDL) will trigger the database engine to automatically commit. It means that if you drop a table, that query will be executed regardless of the transaction.
Ref-Mysql:
    http://dev.mysql.com/doc/refman/5.0/en/implicit-commit.html
2014-12-03 02:39:35
http://php5.kiev.ua/manual/ru/pdo.begintransaction.html
The nested transaction example here is great, but it's missing a key piece of the puzzle.  Commits will commit everything, I only wanted commits to actually commit when the outermost commit has been completed.  This can be done in InnoDB with savepoints.

<?php

class Database extends PDO
{

    protected 
$transactionCount 0;

    public function 
beginTransaction()
    {
        if (!
$this->transactionCounter++) {
            return 
parent::beginTransaction();
        }
       
$this->exec('SAVEPOINT trans'.$this->transactionCounter);
        return 
$this->transactionCounter >= 0;
    }

    public function 
commit()
    {
        if (!--
$this->transactionCounter) {
            return 
parent::commit();
        }
        return 
$this->transactionCounter >= 0;
    }

    public function 
rollback()
    {
        if (--
$this->transactionCounter) {
           
$this->exec('ROLLBACK TO trans'.$this->transactionCounter 1);
            return 
true;
        }
        return 
parent::rollback();
    }
   
}
2015-02-08 10:34:51
http://php5.kiev.ua/manual/ru/pdo.begintransaction.html
please fix in answer #116669:

    $this->exec('ROLLBACK TO trans'.$this->transactionCounter + 1);

with

    $this->exec('ROLLBACK TO trans'.($this->transactionCounter + 1));
2016-04-05 16:35:53
http://php5.kiev.ua/manual/ru/pdo.begintransaction.html
OK I'm finding a solution for "NESTED" transactions in MySQL, and as you know in the MySQL documentation says that it's not possible to have transactions within transactions. I was trying to use the Database class propossed here in pdo.begintransaction but unfortunately that's wrong for many things related to the control flow that I have been solved with the following code (LOOK THE EXAMPLE AT THE END, CarOwner)

<?php

class TransactionController extends \\PDO {
    public static 
$warn_rollback_was_thrown false;
    public static 
$transaction_rollbacked false;
    public function 
__construct()
    {
       
parent :: __construct( ... connection info ... );
    }
    public static 
$nest 0;
    public function 
reset()
    {
       
TransactionController :: $transaction_rollbacked false;
       
TransactionController :: $warn_rollback_was_thrown false;
       
TransactionController :: $nest 0;
    }
    function 
beginTransaction()
    {
       
$result null;
        if (
TransactionController :: $nest == 0) {
           
$this->reset();
           
$result $this->beginTransaction();
        }
       
TransactionController :: $nest++;
        return 
$result;
    }

    public function 
commit()
    {

       
$result null;

        if (
TransactionController :: $nest == &&
                !
TransactionController :: $transaction_rollbacked &&
                !
TransactionController :: $warn_rollback_was_thrown) {
                   
$result parent :: commit();
                }
               
TransactionController :: $nest--;
                return 
$result;
    }

    public function 
rollback()
    {
       
$result null;
        if (
TransactionController :: $nest >= 0) {
            if (
TransactionController :: $nest == 0) {
               
$result parent :: rollback();
               
TransactionController :: $transaction_rollbacked true;
            }
            else {
               
TransactionController  :: $warn_rollback_was_thrown true;
            }
        }
       
TransactionController :: $nest--;
        return 
$result;
    }

    public function 
transactionFailed()
    {
        return 
TransactionController :: $warn_rollback_was_thrown === true;
    }
   
// to force rollback you can only do it from $nest = 0
   
public function forceRollback()
    {
        if (
TransactionController :: $nest === 0) {
           
throws new \PDOException();
        }
    }
}

?>
2016-10-02 01:12:46
http://php5.kiev.ua/manual/ru/pdo.begintransaction.html
When transaction gets your enemy:

try {

$pdo->beginTransaction();

// ... whatever db actions you have and then:

$sql_insert = 'INSERT INTO a (a_id, a_name, a_date)
  VALUES (:a_id, :a_name CURRENT_TIMESTAMP)';

  $stmt = $pdo->prepare($sql_insert);

  $stmt->bindValue(':_id', $id);
  $stmt->bindValue(':a_name', $name);

  $stmt->execute();

  $pdo->commit();

} catch (\PDOException $e) {

  $pdo->rollBack();

  http_response_code(500);
  header("Content-type: text/plain");
  echo $e;
  exit();

}

Notice the sytax error? There's a missing comma that just slips through every gate of surveillance:

"...UES (:a_id, :a_name CURR..." :: between the to be binded value and current timestamp command. The missing comma will not generate any error no syntax error, no database error. Though this record will never or partially make it. This will just silently fail. The story gets rough if you build your other dependencies on the transactions unmovable foot-stones of trust.

Just imagine you have some basic records in your transaction that you really wanna ensure ends up correctly in your db. And these records have a foreign constraint relation with another record. (That of course if you'd include in the transaction would throw you a big red error(–for who don't get it: transaction will first attempt to write everything into a temporary place, where it will get checked before everything gets into its permanent place, so constraints cannot be checked for something that don't exist yet.)) So you have a constraint that's based on the trust PDO will throw an error if something gets screwed, so what, you'll just place that insertion after the commit was successful. (Without any doublecheck.)

What happens you will get some partial records in your database without a relation that just might be important. You will that way be able to build up very sophisticated strange behaviours in your application. So pay attention!
2016-11-17 11:29:22
http://php5.kiev.ua/manual/ru/pdo.begintransaction.html
Автор:
A way to use transaction and prepared statement to speed-up bulk INSERTs :

<?php

// ...

$insert $pdo->prepare('INSERT INTO table (c1, c2, c3) VALUES (?, ?, ?)');
$bulk 3_000// To adjust according to your data/system
$rows 0;

$pdo->beginTransaction();
while (
$entry fgetcsv($fp)) {
   
$insert->execute($entry);
    if (++
$rows $bulk === 0) {
       
$pdo->commit();
       
$pdo->beginTransaction();
    }
}
if (
$pdo->inTransaction()) { // Remaining rows insertion
   
$pdo->commit();
}
2021-09-18 15:34:44
http://php5.kiev.ua/manual/ru/pdo.begintransaction.html

    Поддержать сайт на родительском проекте КГБ