我正在创建一个类,该类将管理与Firebird数据库的连接。将安装火鸟服务,以方便多次连接数据库。不幸的是,我的软件将要部署的环境可能很不稳定,我不能总是保证当我尝试连接时Firebird服务会运行,或者在我建立连接后它会继续运行。
为了集中错误处理,我决定代码的不同部分无论如何都不会直接使用数据库游标。相反,我将从连接管理器中公开query()
和dml()
方法。考虑到下面的代码(为了简洁起见,没有包括一些代码),这在一定程度上是有效的。
class DBConnection(object):
# self._conn is an instance of kinterbasdb.connect()
def query(self, query, params = None):
cursor = self._conn.cursor()
if params:
cursor.execute(query, params)
else:
cursor.execute(query)
return [[x[0].title() for x in cursor.description]] + [r for r in cursor.fetchall()]
def dml(self, query, params = None):
cursor = self._conn.cursor()
if params:
cursor.execute(query, params)
else:
cursor.execute(query)
self._conn.commit()
当Firebird服务停止或由于某种原因无法访问时,故障就会显现出来。我预计self._conn.cursor()
会抛出一个异常,这将使做这样的事情变得简单:
class DBConnection(object):
# self._conn is an instance of kinterbasdb.connect()
def cursor(self):
try:
return self._conn.cursor()
except:
# Error handling code here, possibly reconnect, display alert.
def query(self, query, params = None):
cursor = self.cursor()
def dml(self, query, params = None):
cursor = self.cursor()
不幸的是,当我请求一个游标时,并没有抛出异常。直到接到cursor.execute()
的电话,我才意识到问题。这意味着,如果我想正确地集中我的错误处理,我必须做这样的事情:
class DBConnection(object):
# self._conn is an instance of kinterbasdb.connect()
def cursor(self):
try:
cursor = self._conn.cursor()
cursor.execute("Select NULL From <sometable>")
return cursor
except:
# Error handling code here, possibly reconnect, display alert.
这需要额外往返于我的数据库,浪费了一个事务(Firebird数据库对数据库生命周期内的总事务有严格的上限),而且通常感觉是错误的。我想知道,有没有人在Python数据库API的其他实现中遇到过类似的情况?如果是,他们是如何克服的?
我正在测试对我的类的以下修改,我相信这些修改将以最小的代码重复实现我想要的集中处理。它们还稍微简化了查询和dml方法,并消除了我想要避免的额外查询(心跳)。
class DBConnection(object):
# self._conn is an instance of kinterbasdb.connect()
def query(self, query, params = None):
cursor = self._conn.cursor()
self.execute(cursor, query, params)
return [[x[0].title() for x in cursor.description]] +
[r for r in cursor.fetchall()]
def dml(self, query, params = None):
cursor = self._conn.cursor()
self.execute(cursor, query, params)
self._conn.commit()
def execute(self, cursor, query, params = None):
try:
if params:
cursor.execute(query, params)
else:
cursor.execute(query)
except Exception, e:
# Handling
与Firebird的DB连接与标准TCP/IP连接一样存在连接检测问题。也就是说,在使用之前,连接不容易被检测为"死"。在TCP/IP世界中,解决这一问题的方法是使用KeepAlives(仍然是大约15分钟的检测周期)和明确的心跳。发送查询以进行检测类似于发出心跳以查看它是否还活着。
在游标上执行SQL语句时,断开连接时会出现异常。检测连接故障的适当位置是在使用点。集中的错误检查是一个很好的目标,但如上所述,在返回有效的游标对象和稍后的执行之间,您仍然可以保留与Firebird的连接丢失的可能性(即,您仍然可能在dml()函数中的execute()调用失败)。