30

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()呼び出しです。ただし、このようなフロー制御に例外処理を使用することは悪い習慣だと聞いています。考え?

4

2 に答える 2

38

ただし、接続できない場合は、それdb以上下に存在しません。これが、上記で設定した理由ですdb = None。しかし、それは良い習慣ですか?

いいえ、設定db = Noneはベストプラクティスではありません。データベースへの接続が機能するか機能しないかの2つの可能性があります。

  • データベースへの接続が機能しません:

    発生した例外がキャッチされ、再発生しなかったため、に到達するまで続行しますcursor = db.Cursor()

    db == None、したがって、に似た例外TypeError: 'NoneType' object has no attribute 'Cursor'が発生します。データベース接続が失敗したときに生成された例外はすでにキャッチされているため、失敗の理由は偽装されています。

    個人的には、すぐに再試行しない限り、常に接続例外を発生させます。どのようにそれを捕まえるかはあなた次第です。エラーが続く場合は、「データベースを確認してください」と電子メールで送信します。

  • データベースへの接続は機能します。

    変数はブロックdbに割り当てられます。メソッドが機能try:... exceptする場合は、接続オブジェクトに置き換えられます。connectdb

いずれにせよ、の初期値dbは使用されません。

ただし、このようなフロー制御に例外処理を使用することは悪い習慣だと聞いています。

他の言語とは異なり、Pythonフロー制御に例外処理を使用します。私の答えの終わりに、私は同様の質問をするStackOverflowとProgrammersに関するいくつかの質問にリンクしました。すべての例で、「Pythonで」という単語が表示されます。

それはあなたが船外に出るべきだと言っているわけではありませんが、Pythonは一般的に「許可よりも許しを求める方が簡単です」というマントラEAFPを使用しています。変数が存在するかどうかを確認するにはどうすればよいですか?の上位3つの投票例 フロー制御を使用するかどうかの良い例です。

ネストの例外は良い考えですか?または、このような依存/カスケード例外を処理するためのより良い方法はありますか?

ネストされた例外には何も問題はありません。これも、正常に実行する限りです。あなたのコードを考えてみましょう。すべての例外を削除して、すべてを1つのtry:... exceptブロックにまとめることができます。例外が発生した場合、それが何であったかはわかりますが、何が悪かったのかを正確に追跡するのは少し困難です。

の失敗について自分自身に電子メールを送りたい場合はどうなりますcursor.executeか?cursor.executeこの1つのタスクを実行するには、例外が発生する必要があります。次に、例外を再発生させて、外側にキャッチされるようにします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()

参考文献:

cx_Oracleドキュメンテーション

例外を通常の制御フローとして使用してみませんか?
Pythonの例外処理はPHPや他の言語よりも効率的ですか?
論理演算子としてtrycatchを使用することに対する賛成または反対の議論

于 2012-03-24T16:23:41.817 に答える
1

別の、おそらく洗練された解決策は、データベース呼び出し関数にデコレータを使用することです。デコレータを使用すると、エラーを修正してデータベース呼び出しを再試行できます。失効した接続の場合、修正は、コールを再接続して再発行することです。これが私のために働いたデコレータです:

####### Decorator named dbReconnect ########
#Retry decorator
#Retries a database function twice when  the 1st fails on a stale connection
def dbReconnect():
    def real_decorator(function):
        def wrapper(*args, **kwargs):
            try:
                return function(*args, **kwargs)
            except  Exception as inst:
                print ("DB error({0}):".format(inst))
                print ("Reconnecting")
                #...Code for reconnection is to be placed here..
                ......
                #..end of code for reconnection
            return function(*args, **kwargs)
        return wrapper
    return real_decorator

###### Decorate the DB Call like this: #####
    @dbReconnect()
    def DB_FcnCall(...):
    ....

Githubの詳細:https ://github.com/vvaradarajan/DecoratorForDBReconnect/wiki

注:接続プールを使用する場合、接続をチェックし、古い場合は接続を更新する内部接続プールの手法も問題を解決します。

于 2016-11-16T15:10:20.573 に答える