PHP + MySQLトランザクションの例


294

MySQLトランザクションが使用されているPHPファイルの通常の例は本当に見つかりません。その簡単な例を見せていただけますか?

そしてもう一つ質問。私はすでに多くのプログラミングを行っており、トランザクションを使用していません。PHP関数などを入れheader.phpて、1つ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");
}

10
mysql_query("BEGIN");シーケンスの代わりに使用できますmysql_query("SET AUTOCOMMIT=0"); mysql_query("START TRANSACTION");
Kirzilla

75
mysql_*新しいコードでは関数を使用しないでください。それらはもはや保守されておらず、正式に非推奨になっています。赤い箱見える?学ぶプリペアドステートメントの代わりに、および使用 PDOまたは MySQLiをする -この記事は、あなたがどれを決めるのに役立ちます。PDOを選択した場合こちらが良いチュートリアルです。
Naftali別名Neal

6
「mysql_query( "SET AUTOCOMMIT = 0");」を実行します すべての接続をコミット機能を待機するように設定しますか、それとも関連する接続のためだけですか?
Hamid

1
@Neal、mysql非推奨であるにもかかわらず実際には死んでしまい、PECLで永久に利用できるようになります。
Pacerier 2014年

2
@Pacerier廃止されるものは「死ぬ」ことはありません。それらはレガシーソフトウェアのために公式に開催されますが、新しいソフトウェアのための推奨された慣行から維持され、影響を受けるのをやめます。事実は残っています、使用しないでくださいmysql
taylorcressy 2014年

回答:


325

トランザクションを扱うときに一般的に使用するアイデアは、次のようになります(半疑似コード)

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 (Exception $e) {
    // An exception has been thrown
    // We must rollback the transaction
    $db->rollback();
}


このアイデアでは、クエリが失敗した場合、例外をスローする必要があることに注意してください。

  • PDOは、構成方法に応じて、それを行うことができます
  • それ以外の場合は、他のAPIを使用して、クエリの実行に使用する関数の結果をテストし、自分で例外をスローする必要があります。


残念ながら、魔法は関与していません。命令をどこかに置いてトランザクションを自動的に実行させることはできません。トランザクションで実行する必要があるクエリのグループを特定する必要があります。

たとえば、トランザクションの前(の前begin)にいくつかのクエリがあり、トランザクションの後に別のクエリがいくつかあることがよくあります。(どちらか後commitrollbackこれらのクエリは、何が起こったかに関係なく実行され、あなたがお勧めします(またはしない)にトランザクション。


35
db以外の例外をスローする可能性のある操作を実行している場合は注意してください。その場合、非dbステートメントからの例外により、(すべてのdb呼び出しが成功した場合でも)誤ってロールバックが発生する可能性があります。通常、エラーがデータベース側でなくてもロールバックは良い考えだと思いますが、サードパーティ/重要でないコードがそれほど重要でない例外を引き起こし、それでも続行したい場合がありますトランザクション。
HalilÖzgür12年

6
$dbここのタイプは何ですか?mysqli?
ジェイク

3
@Jake mysqliを使用する例については、私の回答を参照してください(Pascalのアプローチとスタイルが似ています)。
EleventyOne 2013

2
PDOException必要に応じて例外値をキャッチおよびチェックするように簡単に変更できます。us2.php.net/PDOException
Yamiko

1
$ dbはPDOオブジェクト(接続)です。参照:php.net/manual/en/pdo.connections.php
フィル

110

私はそれを理解したと思います、それは正しいですか?:

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");
}

26
autocommit = 0を設定する必要はありません。トランザクションは常にそのように機能します。
bgcode

2
@babonk-これがInnoDBの場合によくわかりませんか?
buggedcom '12年

6
トランザクションを開始すると、AUTOCOMMIT = 0
bgcode

4
@babonkは正しいです。トランザクションが開始されると、AUTOCOMMIT = 0が暗黙的に設定され、コミットまたはロールバックによってトランザクションが終了した後、MySqlはトランザクションを開始する前に使用されていたAUTOCOMMIT値を元に戻します。注:AUTOCOMMIT = 0を設定しないでください。変更をコミットした後、別の行を挿入/更新する場合は、明示的にコミットする必要があるためです。
eroteev 2013年

4
エンジンストアはMyISAMではなくInnoDBである必要があります。
javad 2016

39
<?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";
}

?>

このような広くて注目度の高い質問の場合、回答にもそれが反映されていればすばらしいでしょう。あなたのコードサンプルは素晴らしいですが、もっと詳しく説明できますか?トランザクションについて、なぜ、いつ、どこで説明しますか?最後に、コードと説明をリンクします。
Dennis Haarbrink、

3
StackOverflowへようこそ。回答には必ず説明文を書いてください。
Adrian Heine、

6
申し訳ありませんが、最初のコードは非常に簡単です-初心者の場合-commit()rollback()begin()をクラスDBに配置します(たとえば)、$ query-一度だけではない-多分$ query0 $ query1-それからそれらを
チェック

20
彼のコメントは例をかなり明確にします。優れたコードでは、テキストを説明する必要はありません。また、質問は簡単な例を求めています。私はこの答えが好きです。
なし

@GedzbergAlex単一クエリの場合、トランザクションの必要はありません。単にトランザクションを混乱させるだけです。単一クエリに対してトランザクションを使用する理由はありますか?
ouɐɯʞɔıɥʇɹɐʞ

35

これは「php mysqlトランザクション」のgoogleでの最初の結果なので、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で立ち往生しているため、コメントできません。


2
関連する注記で、InnoDBテーブルを使用している場合、トランザクションにautocomitt()アプローチを使用するとテーブルをロック/ロック解除できるが、begin_transaction()アプローチを使用している場合は不可能であることを発見しました:MySQLドキュメント
EleventyOne 2013

+1は、実際のmysqliコードを使用した詳細な(およびコメント付きの)例です。これをありがとう。そして、ロック/トランザクションに関するあなたのポイントは確かに非常に興味深いものです。
a.real.human.being 2013

1
「autocommit(FALSE)」は同じデータベース/テーブルの別の接続に影響しますか?つまり、2つのページを開いて、そのうちの1つが "autocommit(FALSE)"に接続を設定したものの、他のページが自動コミット機能を離れた場合、コミット機能を待機するかどうかを意味します。自動コミットが接続の属性であり、データベース/テーブルの属性ではないかどうかを知りたい。ありがとう
Hamid

2
$conn->autocommit(FALSE)上記の例の@Hamid は、個々の接続にのみ影響を及ぼします。データベースへの他の接続には影響しません。
EleventyOne 2014年

1
注:クエリがfalseまたは0と評価される有効な結果を返すことができる場合if (!result)if (result === false)、の代わりにを実行してください。
ToolmakerSteve

10

使用しているストレージエンジンを確認してください。MyISAMの場合Transaction('COMMIT','ROLLBACK')、サポートされるのはMyISAMではなくInnoDBストレージエンジンのみであるため、トランザクションはサポートされません。


7

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関連の回答から判断すると、間違いを犯しやすいからです。最も一般的なものは、例外の再スローを忘れてtryブロック内でトランザクションを開始することです。


5

クエリのベクトルを取得してトランザクションを実行する関数を作成しました。多分誰かがそれを知っているでしょう:

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;
        }
    }

3

私はこれを持っていましたが、これが正しいかどうかはわかりません。これも試してみました。

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/


正しくないコード。trigger_errorはtrueを返します(呼び出しに失敗した場合を除く)。したがって、$ resultは常にtrueになるため、このコードは失敗したクエリを見逃し、常にコミットを試みます。同様に厄介なことに、を使用するチュートリアルにリンクしているにもかかわらず、mysql_queryを使用する代わりに、古い非推奨のを使用mysqliしていますmysqli。私見、この悪い例を削除するか、phpknowhowチュートリアルで書かれたコードを使用するようにそれを置き換える必要があります。
ToolmakerSteve 2016

2

のもう1つの手続き型スタイルの例では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。それに興味がある人は、他の場所でググってもっと良い例を見つけましょう。
ToolmakerSteve 2016
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.