通过自定义上下文管理器类使用 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()
忽略错误处理方面的不良实践,提交和回滚是否会按预期工作,还是必须以不同的方式组织它?
上述模式似乎按预期工作(如果测试,则回滚,如果没有则提交,如果异常则回滚)。我不完全确定为什么,所以在这方面的任何启发都会有所帮助。谢谢!
它之所以有效,是因为您编写它是为了处理所有可能性。然而,奇怪的是您没有使用在上下文管理器中创建的对象。
考虑可能性:
1.测试成功:
执行
commit
。 (如果提交抛出异常,它将在您的提交函数中捕获并处理)rollback
。 (但请注意,您是在上下文管理器之外完成此操作,这有点奇怪,因为连接(至少对于光标)已关闭)所以所有案件都得到处理。需要注意的一件事是,您可能想要记录一些内容,让用户知道您未能提交并执行了回滚。
还值得注意的是,以下语法:
如果您想像在下一行中那样使用上下文管理器,那么
database_connection = DatabaseConnection(...)
我没有意义。您应该能够将这两行压缩为简单的 with DatabaseConnection(...) as cursor:
,然后用 database_connection
替换上下文管理器中 cursor
的用法。这就是首先使用上下文管理器的要点。不要在与数据库具有“相同”连接的两个不同对象之间进行混合和匹配,其中一个对象是上下文管理的,而另一个对象不是。这是糟糕的编码,而且非常不符合 Python 标准,并且可能会产生非常意外的结果。
这提出了 @snakecharmerb 的观点,即您应该考虑将回滚放入
__exit__
代码中。就目前情况而言,您基本上没有使用上下文管理器。