Подтвердить что ты не робот

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

Ответ 1

Эта ссылка показывает различные типы ошибок, которые вы можете поймать. MySQLdb.Error - стандартный базовый класс, из которого выводятся все другие ошибки MySQL.

Обычно я использую MySQLdb.Error, потому что он позволяет сосредоточиться на ошибках, связанных с MySQLdb. Напротив, StandardError поймает почти все исключения (не то, что вам нужно, если вы хотите улучшить возможности отладки). Кроме того, использование MySQLdb.Error позволяет отображать точное сообщение об ошибке ( MySQL номер ошибки и все), чтобы вы могли быстрее его отлаживать.

При переходе к первой части вопроса в случае операторов базы данных (обычно) необходимо выполнить транзакции возврата (если они поддерживаются) в случае ошибки.

Методология, которой я следую, заключается в том, чтобы обернуть каждый оператор execute в предложение try except (catching MySQLdb.Error) и использовать откат, если перед печатью сообщения об ошибке и выходом произойдет ошибка.

Однако есть улов. В MySQLdb изменения, которые вы делаете в БД, действительно не, фактически записаны в базу данных до тех пор, пока вы не совершите логический вызов. Таким образом, логически отката не требуется.

В качестве примера,

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

Если вы выходите из программы без коммитовки, значение в БД все равно будет 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()

Ответ 2

IMHO, вы должны откатывать транзакции, если вы продолжаете использовать одно и то же соединение. Остается все до того, как ошибка завершится, когда вы закончите транзакции. Для исключения для catch я всегда использую MySQLdb.Error, но я не уверен, что это правильно.

Ответ 3

Советуется обернуть 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