Примеры транзакций PHP MySQL

#php #mysql #pdo

#php #mysql #транзакции

Вопрос:

Я действительно не нашел нормального примера PHP-файла, в котором используются транзакции MySQL. Можете ли вы показать мне простой пример этого?

И еще один вопрос. Я уже много программировал и не использовал транзакции. Могу ли я поместить в это PHP-функцию или что-то в header.php этом роде, если один mysql_query из них выходит из строя, другие тоже выходят из строя?


Я думаю, что я понял это, правильно ли это?:

 mysql_query("SET AUTOCOMMIT=0");
mysql_query("START TRANSACTION");

$a1 = mysql_query("INSERT INTO rarara (l_id) VALUES('1')");
$a2 = mysql_query("INSERT INTO rarara (l_id) VALUES('2')");

if ($a1 and $a2) {
    mysql_query("COMMIT");
} else {        
    mysql_query("ROLLBACK");
}
 

Комментарии:

1.Вы можете использовать mysql_query("BEGIN"); вместо последовательности mysql_query("SET AUTOCOMMIT=0"); mysql_query("START TRANSACTION");

2. Пожалуйста, не используйте mysql_* функции в новом коде . Они больше не поддерживаются и официально устарели . Видишь красную коробку ? Вместо этого изучите подготовленные инструкции и используйте PDO или MySQLi эта статья поможет вам решить, что именно. Если вы выберете PDO, вот хороший учебник .

3. Устанавливает ли «mysql_query («SET AUTOCOMMIT= 0″);» все соединения для ожидания функции фиксации или это только для связанного с ней соединения?

4. @Neal, На самом деле mysql wun die, несмотря на то, что он устарел, он будет доступен в PECL навсегда.

5. @Pacerier Устаревшие вещи не «умирают». Они официально поддерживаются для устаревшего программного обеспечения, но перестают поддерживаться и исключаются из любых рекомендуемых практик для нового программного обеспечения. Факт остается фактом, не используйте mysql

Ответ №1:

Идея, которую я обычно использую при работе с транзакциями, выглядит следующим образом (полу-псевдокод)::

 try {
    // First of all, let's begin a transaction
    $db->beginTransaction();
    
    // A set of queries; if one fails, an exception should be thrown
    $db->query('first query');
    $db->query('second query');
    $db->query('third query');
    
    // If we arrive here, it means that no exception was thrown
    // i.e. no query has failed, and we can commit the transaction
    $db->commit();
} catch (Throwable $e) {
    // An exception has been thrown
    // We must rollback the transaction
    $db->rollback();
    throw $e; // but the error must be handled anyway
}
 

Обратите внимание, что, согласно этой идее, в случае сбоя запроса должно быть выдано исключение:

  • PDO может это сделать, в зависимости от того, как вы его настроите
  • в противном случае, с помощью какого-либо другого API, вам, возможно, придется протестировать результат функции, используемой для выполнения запроса, и самостоятельно создать исключение.

К сожалению, здесь нет никакой магии. Вы не можете просто поместить куда-нибудь инструкцию и автоматически выполнять транзакции: вам все равно нужно указать, какая группа запросов должна выполняться в транзакции.

Например, довольно часто у вас будет пара запросов перед транзакцией (до begin ) и еще пара запросов после транзакции (после или commit или rollback ), и вы захотите, чтобы эти запросы выполнялись независимо от того, что произошло (или нет) в транзакции.

Комментарии:

1. Будьте осторожны, если вы выполняете операции, которые могут вызывать исключения, отличные от db. Если это так, исключение из инструкции, отличной от db, может непреднамеренно вызвать откат (даже если все вызовы db успешны). Обычно вы думаете, что откат — хорошая идея, даже если ошибка была не на стороне БД, но бывают случаи, когда сторонний / некритичный код может вызвать не очень важные исключения, и вы все равно хотите продолжить транзакцию.

2. Какой здесь $db тип? mysqli?

3. @Jake Смотрите Мой ответ для примера, который использует mysqli (похожий по стилю на подход Pascal).

4. его можно легко модифицировать, чтобы перехватывать PDOException и даже проверять значения исключений, если это необходимо. us2.php.net/PDOException

5. $db — это объект PDO (соединение). Ссылка: php.net/manual/en/pdo.connections.php

Ответ №2:

Я думаю, что я понял это, правильно ли это?:

 mysql_query("START TRANSACTION");

$a1 = mysql_query("INSERT INTO rarara (l_id) VALUES('1')");
$a2 = mysql_query("INSERT INTO rarara (l_id) VALUES('2')");

if ($a1 and $a2) {
    mysql_query("COMMIT");
} else {        
    mysql_query("ROLLBACK");
}
 

Комментарии:

1. Хранилище движка должно быть InnoDB, а не MyISAM!

Ответ №3:

 <?php

// trans.php
function begin(){
    mysql_query("BEGIN");
}

function commit(){
    mysql_query("COMMIT");
}

function rollback(){
    mysql_query("ROLLBACK");
}

mysql_connect("localhost","Dude1", "SuperSecret") or die(mysql_error());

mysql_select_db("bedrock") or die(mysql_error());

$query = "INSERT INTO employee (ssn,name,phone) values ('123-45-6789','Matt','1-800-555-1212')";

begin(); // transaction begins

$result = mysql_query($query);

if(!$result){
    rollback(); // transaction rolls back
    echo "transaction rolled back";
    exit;
}else{
    commit(); // transaction is committed
    echo "Database transaction was successful";
}

?>
 

Комментарии:

1. Для широкого и громкого вопроса, подобного этому, было бы здорово, если бы ответы также отражали это. Ваш пример кода великолепен, но не могли бы вы подробнее рассказать? Объясните о транзакциях, почему, когда и где? Наконец, свяжите код с вашим объяснением.

2. Добро пожаловать в StackOverflow. Пожалуйста, всегда пишите какой-нибудь описывающий текст к своему ответу.

3. извините, что я начинающий, и мой плохой английский, это очень простой пример кода — для начинающих — commit() rollback() begin() помещается в класс DB (например), $query — не один раз — может быть, $query0 $ query1 — затем проверяйте их — я использую этот код, этот самый легко понять =)

4. Его комментарии делают пример довольно ясным. Хороший код не должен нуждаться в описывающем тексте. Также в вопросе задается простой пример. Мне нравится этот ответ.

5. @GedzbergAlex для одного запроса нет необходимости в транзакции, просто это сбивает с толку транзакцию, есть ли причина использовать транзакцию для одного запроса?

Ответ №4:

Поскольку это первый результат в Google для «транзакции php mysql», я подумал, что добавлю ответ, который явно демонстрирует, как это сделать с помощью mysqli (как и хотел первоначальный автор примеров). Вот упрощенный пример транзакций с PHP / mysqli:

 // let's pretend that a user wants to create a new "group". we will do so
// while at the same time creating a "membership" for the group which
// consists solely of the user themselves (at first). accordingly, the group
// and membership records should be created together, or not at all.
// this sounds like a job for: TRANSACTIONS! (*cue music*)

$group_name = "The Thursday Thumpers";
$member_name = "EleventyOne";
$conn = new mysqli($db_host,$db_user,$db_passwd,$db_name); // error-check this

// note: this is meant for InnoDB tables. won't work with MyISAM tables.

try {

    $conn->autocommit(FALSE); // i.e., start transaction

    // assume that the TABLE groups has an auto_increment id field
    $query = "INSERT INTO groups (name) ";
    $query .= "VALUES ('$group_name')";
    $result = $conn->query($query);
    if ( !$result ) {
        $result->free();
        throw new Exception($conn->error);
    }

    $group_id = $conn->insert_id; // last auto_inc id from *this* connection

    $query = "INSERT INTO group_membership (group_id,name) ";
    $query .= "VALUES ('$group_id','$member_name')";
    $result = $conn->query($query);
    if ( !$result ) {
        $result->free();
        throw new Exception($conn->error);
    }

    // our SQL queries have been successful. commit them
    // and go back to non-transaction mode.

    $conn->commit();
    $conn->autocommit(TRUE); // i.e., end transaction
}
catch ( Exception $e ) {

    // before rolling back the transaction, you'd want
    // to make sure that the exception was db-related
    $conn->rollback(); 
    $conn->autocommit(TRUE); // i.e., end transaction   
}
 

Кроме того, имейте в виду, что в PHP 5.5 появился новый метод mysqli::begin_transaction. Однако это еще не было задокументировано командой PHP, и я все еще застрял в PHP 5.3, поэтому я не могу это комментировать.

Комментарии:

1. В связи с этим я только что обнаружил, что если вы работаете с таблицами InnoDB, можно блокировать / разблокировать таблицы при использовании подхода autocomitt() к транзакциям, но это НЕВОЗМОЖНО при использовании подхода begin_transaction(): документация MySQL

2. 1 для подробного (и прокомментированного) примера с фактическим кодом mysqli. Спасибо за это. И ваша точка зрения о блокировке / транзакциях действительно очень интересна.

3. Влияет ли «автоматическая фиксация (FALSE)» на другое соединение в той же базе данных / таблице? Я имею в виду, что если мы откроем две страницы, то одна из них установит свое соединение с «autocommit (FALSE)», а другая покинет функцию автоматической фиксации, будет ли она ждать функции фиксации или нет. Я хочу знать, является ли автоматическая фиксация атрибутом для соединений, а не для базы данных / таблицы. Спасибо

4. @Hamid $conn->autocommit(FALSE) , в приведенном выше примере, влияет только на отдельное соединение — оно не влияет ни на какие другие подключения к базе данных.

5. ПРИМЕЧАНИЕ: вместо if (!result) , следует делать if (result === false) , если запрос способен возвращать действительный результат, который будет равен false или нулю.

Ответ №5:

Пожалуйста, проверьте, какой механизм хранения вы используете. Если это MyISAM, то Transaction('COMMIT','ROLLBACK') не будет поддерживаться, потому что транзакции поддерживает только механизм хранения InnoDB, а не MyISAM.

Ответ №6:

При использовании подключения PDO:

 $pdo = new PDO('mysql:host=localhost;dbname=mydb;charset=utf8', $user, $pass, [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // this is important
]);
 

Я часто использую следующий код для управления транзакциями:

 function transaction(Closure $callback)
{
    global $pdo; // let's assume our PDO connection is in a global var

    // start the transaction outside of the try block, because
    // you don't want to rollback a transaction that failed to start
    $pdo->beginTransaction(); 
    try
    {
        $callback();
        $pdo->commit(); 
    }
    catch (Exception $e) // it's better to replace this with Throwable on PHP 7 
    {
        $pdo->rollBack();
        throw $e; // we still have to complain about the exception
    }
}
 

Пример использования:

 transaction(function()
{
    global $pdo;

    $pdo->query('first query');
    $pdo->query('second query');
    $pdo->query('third query');
});
 

Таким образом, код управления транзакциями не дублируется по всему проекту. Что хорошо, потому что, судя по другим PDO-ralated ответам в этой теме, в нем легко ошибиться. Наиболее распространенными из них являются то, что вы забываете повторно создать исключение и запускаете транзакцию внутри try блока.

Ответ №7:

Я создал функцию для получения вектора запросов и выполнения транзакции, может быть, кто-то сочтет это полезным:

 function transaction ($con, $Q){
        mysqli_query($con, "START TRANSACTION");

        for ($i = 0; $i < count ($Q); $i  ){
            if (!mysqli_query ($con, $Q[$i])){
                echo 'Error! Info: <' . mysqli_error ($con) . '> Query: <' . $Q[$i] . '>';
                break;
            }   
        }

        if ($i == count ($Q)){
            mysqli_query($con, "COMMIT");
            return 1;
        }
        else {
            mysqli_query($con, "ROLLBACK");
            return 0;
        }
    }
 

Ответ №8:

У меня было это, но я не уверен, правильно ли это. Также можно попробовать это.

 mysql_query("START TRANSACTION");
$flag = true;
$query = "INSERT INTO testing (myid) VALUES ('test')";

$query2 = "INSERT INTO testing2 (myid2) VALUES ('test2')";

$result = mysql_query($query) or trigger_error(mysql_error(), E_USER_ERROR);
if (!$result) {
$flag = false;
}

$result = mysql_query($query2) or trigger_error(mysql_error(), E_USER_ERROR);
if (!$result) {
$flag = false;
}

if ($flag) {
mysql_query("COMMIT");
} else {        
mysql_query("ROLLBACK");
}
 

Идея отсюда: http://www.phpknowhow.com/mysql/transactions /

Комментарии:

1. Неправильный код. trigger_error вернет true (если вы не ошиблись с вызовом), поэтому $result всегда будет true , поэтому этот код будет пропускать любой неудачный запрос и всегда пытаться зафиксировать. Не менее тревожно и то, что вы используете старое устаревшее mysql_query , вместо того чтобы использовать mysqli , даже если вы ссылаетесь на учебное пособие, которое использует mysqli . ИМХО, вам следует либо удалить этот плохой пример, либо заменить его, чтобы использовать код, как написано в руководстве phpknowhow.

Ответ №9:

Еще один пример процедурного стиля с mysqli_multi_query , предполагает $query заполнение операторами, разделенными точкой с запятой.

 mysqli_begin_transaction ($link);

for (mysqli_multi_query ($link, $query);
    mysqli_more_results ($link);
    mysqli_next_result ($link) );

! mysqli_errno ($link) ?
    mysqli_commit ($link) : mysqli_rollback ($link);
 

Комментарии:

1. Несколько странный код, но, по крайней мере, он предполагает использование mysqli_multi_query . Любой, кто интересуется этим, должен поискать в другом месте более чистый пример.

2. mysqli_multi_query() не рекомендуется, если в запросах есть какие-либо предоставленные пользователем данные. Подготовленные инструкции не поддерживаются в этой функции.