cx_Oracle和异常处理 - 良好实践?

vic*_*ooi 25 python oracle 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()
Run Code Online (Sandbox Code Playgroud)

但是,我不太确定这里的异常处理的最佳设计是什么.

首先,我db在try块中创建对象,以捕获任何连接错误.

但是,如果它无法连接,那么db将不再存在 - 这就是我在db = None上面设置的原因.但是,这是一种好的做法吗?

理想情况下,我需要捕获连接错误,然后运行DDL语句时出错,等等.

嵌套异常是个好主意吗?或者有更好的方法来处理这样的依赖/级联异常?

此外,还有一些部分(例如连接失败),我希望脚本只是终止 - 因此注释掉了sys.exit()调用.但是,我听说使用异常处理进行流量控制是不好的做法.思考?

Ben*_*Ben 32

但是,如果它无法连接,那么db将不再存在 - 这就是我在db = None上面设置的原因.但是,这是一种好的做法吗?

不,设置db = None不是最佳实践.有两种可能性,连接数据库都可以使用,也可以不运行.

  • 连接到数据库不起作用:

    由于引发的异常已经被抓住而没有被重新提起,所以你会一直持续到达cursor = db.Cursor().

    db == None因此,TypeError: 'NoneType' object has no attribute 'Cursor'将提出类似的例外情况.由于已捕获数据库连接失败时生成的异常,因此伪装失败的原因.

    就个人而言,我总是提出一个连接异常,除非你很快再试一次.你如何抓住它取决于你; 如果错误仍然存​​在,我会发电子邮件说"去检查数据库".

  • 连接到数据库确实有效:

    变量dbtry:... except块中分配.如果connect方法确实有效,则db替换为连接对象.

无论哪种方式,db都不会使用初始值.

但是,我听说使用异常处理进行流量控制是不好的做法.

与其他语言不同,Python 确实使用异常处理进行流控制.在我的回答结束时,我已经链接到有关Stack Overflow和程序员的几个问题,他们提出了类似的问题.在每个例子中,你都会看到"但在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()
Run Code Online (Sandbox Code Playgroud)

然后叫它:

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()
Run Code Online (Sandbox Code Playgroud)

进一步阅读:

cx_Oracle 文件

为什么不将异常用作常规控制流?
python异常处理比PHP和/或其他语言更有效吗?
支持使用try catch作为逻辑运算符的参数