我正在使用MySQLdb使用python连接到MySQL。我的表都是InnoDB,我正在使用交易。
我正在努力想出一种跨职能“共享”交易的方法。考虑以下伪代码:
def foo():
db = connect()
cur = db.cursor()
try:
cur.execute(...)
conn.commit()
except:
conn.rollback()
def bar():
db = connect()
cur = db.cursor()
try:
cur.execute(...)
foo() # note this call
conn.commit()
except:
conn.rollback()
在我的代码中的某些位置,我需要致电foo()
,在某些时候我需要致电bar()
。这里的最佳做法是什么?如果在foo()
之外但在commit()
之内呼叫,我如何通知bar()
至bar()
的来电?如果有多个线程调用foo()
和bar()
并且对connect()
的调用没有返回相同的连接对象,那么这显然会更复杂。
更新
我找到了一个适合我的解决方案。我在调用时包装connect()
来增加一个值。调用commit()
会减少该值。如果调用commit()
并且该计数器的> 0,没有提交,值减少。你因此得到了这个:
def foo():
db = connect() # internal counter = 1
...
db.commit() # internal counter = 0, so commit
def bar():
db = connect() # internal counter = 1
...
foo() # internal counter goes to 2, then to 1 when commit() is called, so no commit happens
db.commit() # internal counter = 0, so commit
答案 0 :(得分:0)
IMO将连接对象传递给foo
和bar
答案 1 :(得分:0)
在函数外声明你的连接,并将它们作为参数传递给函数
foo(cur, conn)
bar(cur, conn)
答案 2 :(得分:0)
在这种情况下,您可以利用Python的默认函数参数:
def foo(cur=None):
inside_larger_transaction = False
if cursor is None:
db = connect()
cur = db.cursor()
inside_larger_transaction = True
try:
cur.execute(...)
if not inside_larger_transaction:
conn.commit()
except:
conn.rollback()
因此,如果bar
正在调用foo
,它只是将游标对象作为参数传递。
并非我在为每个小函数创建不同的游标对象时都没有多大意义 - 您应该将多个函数编写为对象的方法,并具有游标属性 - 或者明确地传递cursos (在这种情况下,使用另一个命名参数来指示当前函数是否是主要事务的一部分)
另一种选择是创建一个context-manager类来进行提交,并封装其中的所有事务 - 因此,您的所有函数都不应该执行事务提交 - 您将同时保留transaction.commit和transaction.rollback调用在此对象的__exit__
方法上。
class Transaction(object):
def __enter__(self):
self.db = connect()
cursor = self.db.cursor()
return cursor
def __exit__(self, exc_type, exc_value, traceback):
if exc_type is None:
self.db.commit()
else:
self.db.rollback()
就这样使用它:
def foo(cursor):
...
def foo(cur):
cur.execute(...)
def bar(cur):
cur.execute(...)
foo(cur)
with Transaction() as cursor:
foo(cursor)
with Transaction() as cursor:
bar(cursor)