Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/mysql/62.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 跨函数共享MySQLdb事务_Python_Mysql_Mysql Python - Fatal编程技术网

Python 跨函数共享MySQLdb事务

Python 跨函数共享MySQLdb事务,python,mysql,mysql-python,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 = conn

我正在使用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()
外部调用
foo()
而不是在
bar()内部调用
commit()
,我将如何告诉调用
foo()
?如果有多个线程调用
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

IMO最干净的方法是将连接对象传递给
foo
bar
声明函数外部的连接,并将它们作为参数传递给函数

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

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

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

另一个选项是创建一个类来进行提交,并封装其中的所有事务-因此,您的任何函数都不应执行事务提交-您将保留此对象的
\uuuuuuu exit\uuu
方法上的transaction.commit和transaction.rollback调用

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)

尝试扩展MySQLdb并走单例/工厂路线(这是我们在项目中所做的)我喜欢事务()想法!虽然根据我对Hoopdady答案的评论,您的方法将在数据存储和模型之间创建一个耦合,这是我试图避免的。可能可以进行一些调整,而不需要显式传递光标…我想过这样做,但我不喜欢它在数据存储和模型之间创建的耦合这将阻止我重构foo()的内部结构以使用NoSQL存储(例如)。我认为在python中不能将变量作为引用传递到函数中…?它们只能作为值传递。。