【问题标题】:Sharing MySQLdb transactions across functions跨函数共享 MySQLdb 事务
【发布时间】:2013-02-04 17:56:36
【问题描述】:

我正在使用 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()。这里的最佳做法是什么?如果在bar() 外部而不是bar() 内部调用,我将如何告诉foo()commit() 的调用?如果有多个线程调用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

【问题讨论】:

  • 尝试扩展 MySQLdb 并走单例/工厂路线(这是我们在我的项目中所做的)

标签: python mysql mysql-python


【解决方案1】:

在这种情况下,您可以利用 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,它只是将光标对象作为参数传入。

并不是说我认为为每个小函数创建一个不同的光标对象没有多大意义——你应该将你的几个函数写成一个对象的方法,并有一个光标属性——或者总是显式地传递光标(在在这种情况下,使用另一个命名参数来指示当前函数是否是主要事务的一部分)

另一种选择是创建一个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)

【讨论】:

  • 我喜欢 Transaction() 的想法!尽管根据我对 Hoopdady 的回答的评论,您的方法是在数据存储和我试图避免的模型之间创建耦合。可能能够进行一些不需要显式传入光标的调整...
【解决方案2】:

IMO 最简洁的方法是将连接对象传递给 foobar

【讨论】:

    【解决方案3】:

    在函数之外声明你的连接并将它们作为参数传递给函数

    foo(cur, conn)
    bar(cur, conn)
    

    【讨论】:

    • 我想过这样做,但我不喜欢它在数据存储和模型之间创建的耦合。这将阻止我重构 foo() 的内部结构以使用 NoSQL 存储(例如)。
    • 我认为在 python 中你不能将变量作为参考传递给函数......?它们仅作为值传递..
    猜你喜欢
    • 1970-01-01
    • 2021-09-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-21
    • 1970-01-01
    • 1970-01-01
    • 2019-11-19
    相关资源
    最近更新 更多