跨功能共享MySQLdb事务

时间:2013-02-19 21:34:50

标签: python mysql mysql-python

我正在使用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

3 个答案:

答案 0 :(得分:0)

IMO将连接对象传递给foobar

的最简洁方式

答案 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)