web-dev-qa-db-ja.com

Python-mysql:トランザクションを明示的にロールバックするタイミング

修正ステートメントがあるとします。

cursor = conn.cursor()
# some code
affected_rows1 = cursor.execute(update_statement1, params1)
# some code
conn.commit()
cursor.close()

コードのブロックをtry ... exceptでラップし、例外が発生したときにトランザクションを明示的にロールバックする必要がありますか?また、ロールバックするためにどのMySQLdb例外をキャッチする必要がありますか?以前はStandardErrorこの場合、しかし今、私はコードのブロックが明示的なロールバックさえ必要とするだろうということをためらっています。

次の例は少し難しいですが、最初の更新ステートメントが成功した場合は、明示的なロールバックが必要であることを理解しています。それでも、この場合、どの例外をキャッチする必要がありますか?

cursor = conn.cursor()
# some code
affected_rows1 = cursor.execute(update_statement1, params1)
# some code
affected_rows2 = cursor.execute(update_statement2, params2)
#some code
conn.commit()
cursor.close()
13
newtover

このリンク は、キャッチできるさまざまなタイプのエラーを示しています。 MySQLdb.Errorは、他のすべてのMySQLエラーの派生元となる標準の基本クラスです。

MySQLdb自体に関連するエラーに集中できるため、通常はMySQLdb.Errorを使用します。対照的に、StandardErrorはほとんどすべての例外をキャッチします(より良いデバッグ機能が必要な場合は必要ありません)。さらに、MySQLdb.Errorを使用すると、正確なエラーメッセージ(MySQLエラー番号など)を表示できるため、デバッグを高速化できます。

質問の最初の部分に来ると、データベースステートメントの場合、エラーの場合にトランザクションをロールバックする必要があります(サポートされている場合)。

私が従う方法は、各executeステートメントをtryexcept句でラップし(MySQLdb.Errorをキャッチ)、エラーが発生した場合はエラーメッセージを出力して終了する前にロールバックを使用することです。

ただし、落とし穴があります。 MySQLdbでは、DBに加えた変更は、明示的にcommitを呼び出すまで、実際にはデータベースに書き込まれません。したがって、論理的には、ロールバックは必要ありません。

例として、

conn = MySQLdb.connection(db=, Host=, passwd=, user=)
cur = conn.cursor()
#Say you have a table X with one entry id = 1 and total = 50
cur.execute("update X set total = 70 where id = 1")
#Actual DB has not yet changed
cur.execute("update X set total = 80 where id = 1")
#Actual DB has still not changed

コミットせずにプログラムを終了した場合、commit()を呼び出したことがないため、DBの値は50のままです。

これはあなたが理想的にそれをする方法です:

conn = MySQLdb.connection(db=, Host=, passwd=, user=)
cur = conn.cursor()
#Say you have a table X with one entry id = 1 and total = 50
try:
    cur.execute("update X set total = 70 where id = 1")
except MySQLdb.Error,e:
    print e[0], e[1]
    conn.rollback()
    cur.close()
    conn.close()
    #print lengthy error description!!
    sys.exit(2)
    #Note: Value in table is still 50
#If you do conn.commit() here, value becomes 70 in table too!!
try:
    cur.execute("update X set total = 80 where id = 1")
except MySQLdb.Error,e:
    print e[0], e[1]
    conn.rollback()
    cur.close()
    conn.close()
    #print lengthy error description!!
    sys.exit(2)
    #Value in DB will be 
    #a) 50 if you didn't commit anywhere
    #b) 70 if you committed after first execute statement
conn.commit()
#Now value in DB is 80!!
cur.close()
conn.close()
14
RedBaron

私見ですが、同じ接続を引き続き使用する場合は、トランザクションをロールバックする必要があります。それ以外の場合は、トランザクションの終了時にエラーがコミットされる前のすべてがコミットされます。キャッチする例外として、私は常にMySQLdb.Errorを使用しますが、それが正しいかどうかはわかりません。

2
Kien Truong