cx_Oracle和异常处理-好的做法?
我正在尝试使用cx_Oracle连接到Oracle实例并执行一些DDL语句:
db = None
try:
db = cx_Oracle.connect('username', 'password', 'hostname:port/SERVICENAME')
#print(db.version)
except cx_Oracle.DatabaseError as e:
error, = e.args
if error.code == 1017:
print('Please check your credentials.')
# sys.exit()?
else:
print('Database connection error: %s'.format(e))
cursor = db.cursor()
try:
cursor.execute(ddl_statements)
except cx_Oracle.DatabaseError as e:
error, = e.args
if error.code == 955:
print('Table already exists')
if error.code == 1031:
print("Insufficient privileges - are you sure you're using the owner account?")
print(error.code)
print(error.message)
print(error.context)
cursor.close()
db.commit()
db.close()
但是,我不确定在这里异常处理的最佳设计是什么。
首先,我db
在try块内创建对象,以捕获任何连接错误。
但是,如果无法连接,则db
不会再存在-这就是我在db = None
上面进行设置的原因。但是,这是好习惯吗?
理想情况下,我需要在连接时捕获错误,然后在运行DDL语句时捕获错误,依此类推。
嵌套异常是个好主意吗?还是有更好的方法来处理像这样的依存/级联异常?
另外,有些地方(例如连接失败)我希望脚本终止,因此注释掉了sys.exit()
调用。但是,我听说将异常处理用于流控制是不好的做法。有什么想法吗?
-
但是,如果无法连接,则
db
不会再存在-这就是我在db = None
上面进行设置的原因。但是,这是好习惯吗?不,设置
db = None
不是最佳实践。有两种可能性,要么连接到数据库将起作用,要么将它不起作用。- 连接到数据库不起作用:
由于引发的异常已被捕获且未重新引发,因此请继续直至到达
cursor = db.Cursor()
。db == None
,因此,TypeError: 'NoneType' object has no attribute 'Cursor'
将引发类似的异常。由于已经捕获了数据库连接失败时生成的异常,因此掩盖了失败的原因。就我个人而言,除非您打算稍后再次尝试,否则我总是会提出连接异常。如何捕捉取决于您自己;如果错误仍然存在,我将通过电子邮件发送“去检查数据库”的电子邮件。
- 连接到数据库确实有效:
该变量
db
在您的try:... except
块中分配。如果该connect
方法有效,则将db
其替换为连接对象。无论哪种方式,
db
都不会使用的初始值。但是,我听说将异常处理用于流控制是不好的做法。
与其他语言不同,Python 确实
将异常处理用于流控制。在回答的最后,我已经链接到有关堆栈溢出和程序员的几个问题,这些问题都提出了类似的问题。在每个示例中,您都会看到“ but but in
Python”一词。这并不是说您应该精打细算,但是Python通常使用EAFP的口头禅:
“请求宽恕比允许容易。” 我如何检查变量是否存在中投票最多的三个示例?很好地说明了如何同时使用和不使用流量控制。嵌套异常是个好主意吗?还是有更好的方法来处理像这样的依存/级联异常?
嵌套异常没有任何问题,只要您明智地执行它即可。考虑您的代码。您可以删除所有异常并将整个内容包装在一个
try:... except
块中。如果引发了异常,那么您就知道它是什么,但是要精确地查明出了什么问题要困难一些。如果您想通过电子邮件发送电子邮件给自己,会发生
cursor.execute
什么?cursor.execute
为了执行这一任务,您应该有一个例外。然后,您重新引发异常,以便将其捕获在外部try:...
。不重新引发代码将导致您的代码继续运行,好像什么都没发生,并且您在外部try:...
处理异常的任何逻辑都将被忽略。最终,所有异常都继承自
BaseException
。另外,有些部分(例如连接失败)我希望脚本终止,因此注释掉了sys.exit()调用。
我添加了一个简单的类以及如何调用它,这大致就是我将要做的事情。如果要在后台运行,那么打印错误是不值得的-
人们不会坐在那里手动寻找错误。无论采用哪种标准方式,都应将其记录下来,并通知适当的人员。由于这个原因,我已经删除了打印内容,并替换为要记录的提醒。当
connect
方法失败并引发异常时,由于将类拆分为多个函数execute
,因此在尝试断开连接后,调用将不会运行且脚本将完成。import cx_Oracle class Oracle(object): def connect(self, username, password, hostname, port, servicename): """ Connect to the database. """ try: self.db = cx_Oracle.connect(username, password , hostname + ':' + port + '/' + servicename) except cx_Oracle.DatabaseError as e: # Log error as appropriate raise # If the database connection succeeded create the cursor # we-re going to use. self.cursor = self.db.cursor() def disconnect(self): """ Disconnect from the database. If this fails, for instance if the connection instance doesn't exist, ignore the exception. """ try: self.cursor.close() self.db.close() except cx_Oracle.DatabaseError: pass def execute(self, sql, bindvars=None, commit=False): """ Execute whatever SQL statements are passed to the method; commit if specified. Do not specify fetchall() in here as the SQL statement may not be a select. bindvars is a dictionary of variables you pass to execute. """ try: self.cursor.execute(sql, bindvars) except cx_Oracle.DatabaseError as e: # Log error as appropriate raise # Only commit if it-s necessary. if commit: self.db.commit()
然后调用它:
if __name__ == "__main__": oracle = Oracle.connect('username', 'password', 'hostname' , 'port', 'servicename') try: # No commit as you don-t need to commit DDL. oracle.execute('ddl_statements') # Ensure that we always disconnect from the database to avoid # ORA-00018: Maximum number of sessions exceeded. finally: oracle.disconnect()
进一步阅读:
为什么不将异常用作常规控制流?
python异常处理是否比PHP和/或其他语言更有效?
支持或反对使用try
catch作为逻辑运算符的参数