在将自定义上下文管理器类与 mysql 连接器一起使用时如何提交或回滚数据库中的更改

How to commit or rollback a change in database when using a custom context manager class with mysql connector

提问人:Philip09 提问时间:8/22/2023 最后编辑:UpAndAdamPhilip09 更新时间:8/24/2023 访问量:81

问:

通过自定义上下文管理器类使用 mysql 连接器时,处理提交或回滚数据库中更改的最佳方法是什么?例如,假设自定义类采用以下形式:

class DatabaseConnection:
    def __init__(self, host, user, password, database_name)
        self.host = host
        self.user = user
        self.passwd = password
        self.database_name = database_name
        self.connection = None

    def __enter__(self) -> Union[mysql.cursor.MySQLCursor, None]:
       try:
          if self.connection is None or not sef.connection.is_connected():
              self.connection = mysql.connect(
                  host = self.host, user=self.user, psswd=self.psswd, db=self.database_name
              )
          cursor = self.connection.cursor()
          return cursor
      except Exception:
          return None

    def __exit__(self, exc_type, exc_val, exc_tb):
        if self.connection is not None: 
            self.connection.close()

    def commit(self):
        if self.connection is not None and self.connection.is_connected():
             try:
                 self.connection.commit()
                 return True
             except:
                 self.connection.rollback()

    def rollback(self):
        if self.connection is not None:
           self.connection.rollback()

然后你想使用它,使用语句,这样你就有了光标。您将如何管理提交或回滚更改,尤其是在引发错误时。例如:with

test = True
database_connection = DatabaseConnection(....)
try:
    with database_connection() as cursor:
        # perform some operations

        if test:
            database_connection.rollback()
        else:
            database_connection.commit()
except Exception as e:
     database_connection.rollback()

忽略错误处理方面的不良做法,提交和回滚是否按预期工作,或者您是否必须以不同的方式组织它?

编辑

上述模式似乎按预期工作(如果测试,则回滚,如果不是,则提交,如果异常,则回滚)。我不完全确定为什么,所以在这方面的任何启示都会有所帮助。谢谢!

python 提交 mysql-connector 回滚 contextmanager

评论

0赞 UpAndAdam 8/23/2023
你不完全确定为什么?为什么你的代码有效?
1赞 Homer512 8/23/2023
你的 try-except 会“吃掉”异常,而不是再次引发它们。用户不知道失败,而是执行了回滚commit
1赞 snakecharmerb 8/23/2023
如果 exc* 参数不为空,则执行回滚。__exit__
0赞 Philip09 8/23/2023
@UpAndAdam是的。我认为我不确定的是提交和回滚。if ... else ...
0赞 Philip09 8/23/2023
@Homer512 在最后的代码中,我记录了最后引发了异常,并将其输出到日志中。

答:

1赞 UpAndAdam 8/23/2023 #1

它之所以有效,是因为您已经编写了它来处理所有可能性。然而,奇怪的是,您没有使用在上下文管理器中创建的对象。

考虑以下可能性:

1.测试成功:
执行。(如果 commit 抛出异常,它将在您的 commit 函数中被捕获和处理)
2. 测试失败:
执行。(请注意,您是在上下文管理器之外执行此操作的,这有点奇怪,因为连接(至少对于光标)已关闭)
3. 抛出异常:
捕获异常并执行回滚。
commitrollback

因此,所有案件都得到了处理。需要注意的一点是,您可能希望记录一些内容,以便让用户知道您未能提交并执行了回滚。
同样值得注意的是,如果要像在下一行中那样使用上下文管理器,则语法没有意义。您应该能够将这两行压缩为简单,然后将上下文管理器中的用法替换为 。这就是首先使用上下文管理器的重点。不要在两个不同的对象之间混合和匹配,这两个对象与数据库的“相同”连接,其中一个是上下文管理的,另一个不是。这是糟糕的编码,而且非常不谒蛇,可能会产生非常意想不到的结果。
database_connection = DatabaseConnection(...)with DatabaseConnection(...) as cursor:database_connectioncursor

然后,这就提出了@snakecharmerb的观点,即您应该考虑将回滚放入代码中。就目前而言,您基本上没有使用上下文管理器。__exit__

评论

0赞 Philip09 8/23/2023
谢谢!我认为那里的问题指出了我误解的地方。database_conection = 的语法是因为对象采用 ,,并且我想稍后使用 with 语句重用该对象进行不同的操作。因此,我将把这个对象传递到其他地方,然后打开他们自己的上下文管理器(我不确定这是否是正确的措辞)。即多个方法/对象将使用 DatabaseConnection ...hostuserpassworddatabasename
0赞 Philip09 8/23/2023
我会在 .qeuerying、插入或删除(在本例中按顺序排列)。你的建议是,与其使用,不如使用我应该能够使用?那会更好/更清晰吗?(这有点让我感到困惑)......cursor#perform some operationsif test: database_connection.rollback()if test: cursor.rollback()
2赞 snakecharmerb 8/24/2023
通常,您会允许异常从上下文管理器中冒出,以便调用代码知道出了问题并可以做出适当的反应。例如,Web 应用可能希望发送 500 响应,而不是 200 响应。但最终这取决于您的具体用例。
1赞 UpAndAdam 8/24/2023
是的。你应该使用 cursor.rollback(),就像我说的你应该消除 DatabaseConnection 对象的重复创建。这样做两次,将一个用于某些位,另一个用于其他位,a. 违背了上下文管理器的目的,b. 可能会导致意外行为,因为它们是不同的对象。我不指望对一个人所做的事情在另一个人身上一定是可见的,等等。
2赞 snakecharmerb 8/24/2023
就我个人而言,如果发生异常,我也会关闭连接并回滚到那里。但是,我不会在上下文管理器中捕获异常,我会让调用代码处理它。这个想法是分离责任。上下文管理器有责任确保连接保持良好状态,而不考虑错误。决定如何处理任何错误的责任通常位于代码的其他地方。__exit__