web-dev-qa-db-ja.com

宣言されたトランザクションで使用されていないが、try ... catchで使用されている場合、ロールバックは何もしませんか?

私の質問は、mysqli::rollbackの使用に関するものです。

宣言されたトランザクションでは使用されないが、try...catchで使用された場合、rollbackは何もしませんか? (autocommitオン)

トランザクションが宣言されていなくても機能する場合、単一のクエリステートメントにrollbackが必要ですか?以下の例を参照してください...

try{
    $sql = "DELETE FROM users WHERE user_id=?";
    $row = run($mysqli, $sql, [$user_id]);
}catch(exception $e){
    $mysqli->rollback();
    error($mysqli);
}

複数のクエリで宣言されたトランザクションがあり、トランザクション内のすべてのクエリが失敗した場合、rollbackは古いトランザクションに影響しますか? rollbackのスコープは何ですか?

1
FamousAv8er

いいえ。ロールバックを機能させるには、宣言されたトランザクションが必要です。データの元の状態をどこかに保持できるようにするには、トランザクションを宣言する必要があります。それ以外の場合、ロールバックするものはありません。

トランザクションなしでロールバックを呼び出しても効果がないことを確認することで、トランザクションの有無にかかわらず、これを自分で簡単にテストできます。

さらに読む
PDO.Transactions

4
Robert Harvey

いいえ。ロールバックを機能させるには、宣言されたトランザクションが必要です。データの元の状態をどこかに保持できるようにするには、トランザクションを宣言する必要があります。それ以外の場合、ロールバックするものはありません。

トランザクションなしでロールバックを呼び出しても効果がないことを確認することで、トランザクションの有無にかかわらず、これを自分で簡単にテストできます。

PDOのトランザクションを宣言する必要があるのは本当かもしれません(PDOは非常に退屈なので、頻繁に使用しないでください)が、通常の普通のmysqli DBクエリmysqli autocommitがfalseに設定されている場合にトランザクションが開始されます、そして何も起こりません成功したcommitコマンド。 mySQL 14.7.2.2 autocommit、Commit、Rollback を参照してください:

SET autocommit = 0のセッション内で自動コミットモードが無効になっている場合、セッションは常にトランザクションを開いています。 COMMITまたはROLLBACKステートメントは現在のトランザクションを終了し、新しいトランザクションが開始されます。

OPがPDOの使用について述べたことは一度もないので、PDO以外の$ 0.02をドロップすると思いました... OPの質問に答えるには:

宣言されたトランザクションで使用されていないが、try ... catchで使用されている場合、ロールバックは何もしませんか? (自動コミットオン)

いいえ、トランザクションを開いていない場合、ロールバックは何もしません-@ robert-harveyが指摘したようにPDO宣言されたトランザクションを介して、または単にautocommitをfalseに設定することによって:

$mysqli->autocommit(FALSE);

その後、commit()コマンドを使用しないと、データベースに何もコミットされません。次に、重要なのは、いつコミットし、いつロールバックするかを理解することだけです。 ..何をしたいか、そしてオートコミットをどこでオンまたはオフにしたかに応じて...

複数のクエリで宣言されたトランザクションがあり、トランザクション内のすべてのクエリが失敗した場合、ロールバックは古いトランザクションに影響しますか?ロールバックの範囲は何ですか?

スコープは現在のトランザクションであり、commit()またはrollback()を呼び出すたびにリセットされます。したがって、開いているトランザクションを開始してからcommit()を使用せずに19(エラーのない)クエリを実行すると、20番目のクエリは失敗し、rollback()を実行するとYES、すべて20クエリはすべて1つのトランザクションとして扱われたため、ロールバックされます。ただし、クエリが成功するたびにcommitを呼び出すと、ロールバックは失敗したクエリにのみ影響します。

国境を越えたDB操作の場合、通常はオールオアナッシングのシナリオであることがわかります。たとえば、データベースへの単一の新しいエントリが複数の依存テーブルに影響を及ぼします。そのような場合、ロールバックは必要ありません(単一のトランザクションで、コミットするかしないか)。つまり、「これらのデータベースコマンドはすべてnullとvoidである」ことを(自分または後でコードを表示している人に)明らかにした場合でも、コミットが失敗した場合はロールバックすることをお勧めします。さらに、トランザクションのループを実行している可能性があり、それらのすべてを拒否せずに1つを拒否する必要があるシナリオがあります(OPが回避したように)。

$mysqli->autocommit(FALSE); // all transactions from here
foreach ($dudes as $id=>$data) {
   $mysqli->query("UPDATE favorites SET last_updated=NOW(), favorite_nba_player='".$data["fav_nba"]."', favorite_food='".$data["fav_food"]."' WHERE dude_id='$id'");
   if ((!$mysqli->affected_rows) or ($mysqli->error) or ($data["fav_nba"] != "Kobe Bryant") or ($data["fav_food"] != "beer")) { // doesn't cut the mustard
      $mysqli->rollback(); // end transaction without commit, and start a new transaction
      echo "Error: information for Dude $id is too weak for this DB.<br>";
      continue; // jump to next iteration of loop (next dude)
   }
   // do another database query...
   // maybe another dozen queries, able to bail out on this loop iteration with a rollback whenever necessary...
   // ....
   // made it to the end of our table updates & inserts, lets make sure it all commits
   if ($mysqli->commmit()) { // new transaction is started regardless. We know all saved to db
      echo "Dude $id is a legit dude; update successful!<br>";
      $saved_dudes++;
   }
   else {
      $mysqli->rollback(); // commit didn't work.  Ensure new transaction is started, and make it clear for anyone viewing/editing the code later
      echo "Dude $id seemed legit, but DB commit failed... ugh!<br>";
   }
$total_dudes++;
}
$mysqli->autocommit(TRUE); // turn autocommit back on - transactions no more
$mysqli->query("INSERT INTO dude_update_log SET total_dudes='$total_dudes', saved_dudes='$saved_dudes'"); // executes immediately as usual
0
Josh888