sql >> Databasteknik >  >> RDS >> Oracle

cx_Oracle och undantagshantering - god praxis?

Men om den inte kan ansluta, db kommer inte att existera längre ner - det är därför jag ställer in db = None ovan. Men är det en bra praxis?

Nej, inställningen db = None är inte bästa praxis. Det finns två möjligheter, antingen fungerar det att ansluta till databasen eller inte.

  • Att ansluta till databasen fungerar inte:

    Eftersom det höjda undantaget har fångats och inte höjts igen fortsätter du tills du når cursor = db.Cursor() .

    db == None , alltså ett undantag som liknar TypeError: 'NoneType' object has no attribute 'Cursor' kommer att höjas. Eftersom undantaget som genererades när databasanslutningen misslyckades redan har fångats, är orsaken till felet förtäckt.

    Personligen skulle jag alltid ta upp ett anslutningsundantag om du inte ska försöka igen inom kort. Hur du fångar det är upp till dig; om felet kvarstår skickar jag ett e-postmeddelande och säger "gå och kolla databasen".

  • Att ansluta till databasen fungerar:

    Variabeln db är tilldelad i ditt try:... except blockera. Om connect Metoden fungerar då db ersätts med anslutningsobjektet.

Hur som helst startvärdet för db används aldrig.

Jag har dock hört att det är dålig praxis att använda undantagshantering för flödeskontroll som detta.

Till skillnad från andra språk gör Python använd undantagshantering för flödeskontroll. I slutet av mitt svar har jag länkat till flera frågor om Stack Overflow och programmerare som ställer en liknande fråga. I varje exempel ser du orden "men i Python".

Därmed inte sagt att du ska gå överbord men Python använder i allmänhet mantrat EAFP, "Det är lättare att be om förlåtelse än om tillåtelse." De tre bästa exemplen i Hur kontrollerar jag om en variabel finns? är bra exempel på hur man både kan använda flödeskontroll eller inte.

Är häckande undantag en bra idé? Eller finns det ett bättre sätt att hantera beroende/kaskadkopplade undantag som detta?

Det är inget fel med kapslade undantag, än en gång så länge du gör det förnuftigt. Tänk på din kod. Du kan ta bort alla undantag och slå in det hela i ett try:... except blockera. Om ett undantag tas upp så vet du vad det var, men det är lite svårare att spåra exakt vad som gick fel.

Vad händer sedan om du själv vill säga e-post om felet i cursor.execute ? Du bör ha ett undantag kring cursor.execute för att utföra denna enda uppgift. Du höjer sedan undantaget igen så att det fångas i ditt yttre try:... . Att inte höja igen skulle resultera i att din kod fortsätter som om ingenting hade hänt och vilken logik du än hade lagt i ditt yttre try:... att hantera ett undantag skulle ignoreras.

I slutändan ärvs alla undantag från BaseException .

Det finns också vissa delar (t.ex. anslutningsfel) där jag vill att skriptet bara ska avslutas - därav det kommenterade sys.exit()-anropet.

Jag har lagt till en enkel klass och hur man kallar den, vilket är ungefär hur jag skulle göra det du försöker göra. Om detta ska köras i bakgrunden är det inte lönt att skriva ut felen - folk kommer inte att sitta där manuellt och leta efter fel. De bör vara inloggade på vilket standardsätt du än är och lämpliga personer meddelas. Jag har tagit bort utskriften av denna anledning och ersatt med en påminnelse om att logga.

Eftersom jag har delat upp klassen i flera funktioner när connect metoden misslyckas och ett undantag höjs till execute samtalet kommer inte att köras och skriptet kommer att slutföras efter ett försök att koppla från.

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()

Kalla det sedan:

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()

Mer läsning:

cx_Oracle dokumentation

Varför inte använda undantag som regelbundet kontrollflöde?
Är python-undantagshantering effektivare än PHP och/eller andra språk?
Argument för eller emot att använda try catch som logiska operatorer



  1. Utlösare för att upprätthålla M-M-förhållande

  2. Reflektion i PLSQL?

  3. Använder du Excel för din databas? Här är varför du bör uppgradera till Access

  4. Vad betyder SQL Select-symbolen || betyda?