Python-mysql:何时显式回滚事务

时间:2012-03-21 09:51:42

标签: python mysql mysql-python

假设我有一个修改声明:

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()

3 个答案:

答案 0 :(得分:13)

This link显示了您可以捕获的各种类型的错误。 MySQLdb.Error是从中派生所有其他MySQL错误的标准基类。

我通常使用MySQLdb.Error,因为它可以让您专注于与MySQLdb本身相关的错误。相比之下,StandardError将捕获几乎所有异常(如果您想要更好的调试功能,则不是您想要的)。此外,使用MySQLdb.Error可以显示确切的错误消息( MySQL 错误编号和所有错误消息),以便您可以更快地调试它。

问题的第一部分,在数据库语句的情况下,(通常)必须在发生错误时回滚事务(如果它们受支持)。

我遵循的方法是将每个execute语句包装在try except子句中(捕获MySQLdb.Error)并在打印错误消息并退出之前如果出现错误则使用回滚。

然而,有一个问题。在MySQLdb中,您对DB所做的更改实际写入数据库,直到您进行了明确的调用提交。因此,从逻辑上讲,回滚不是必需的。

举个例子,

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

如果退出程序而不提交,则DB中的值仍为50,因为您从未调用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
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()

答案 1 :(得分:2)

恕我直言,如果继续使用相同的连接,则应该回滚事务。否则,在完成事务后,错误之前的所有内容都将被提交。 对于捕获的异常,我总是使用MySQLdb.Error,但我不确定这是否正确。

答案 2 :(得分:-1)

建议将execute()包装在sub中。我就是这样做的。

def executeSQL(self, stmt):
    cursor = self.dbHand.cursor()

    if not stmt.endswith(";"):
        stmt += ';'

    try:
        cursor.execute(stmt)
    except MySQLdb.Error, e:
        self.logger.error("Caught MYSQL exception :%s: while executing stmt :%s:.\n"%(e,stmt))
        return False