sql >> Database teknologi >  >> RDS >> Oracle

cx_Oracle og undtagelseshåndtering - god praksis?

Men hvis den ikke kan oprette forbindelse, så db vil ikke eksistere længere nede - derfor satte jeg db = None over. Men er det god praksis?

Nej, indstilling db = None er ikke bedste praksis. Der er to muligheder, enten vil forbindelse til databasen fungere, eller også vil den ikke.

  • Tilslutning til databasen virker ikke:

    Da den hævede undtagelse er blevet fanget og ikke re-raiset, fortsætter du, indtil du når cursor = db.Cursor() .

    db == None , så en undtagelse, der ligner TypeError: 'NoneType' object has no attribute 'Cursor' vil blive hævet. Da undtagelsen, der blev genereret, da databaseforbindelsen mislykkedes, allerede er blevet fanget, er årsagen til fejlen skjult.

    Personligt vil jeg altid rejse en forbindelsesundtagelse, medmindre du snart vil prøve igen. Hvordan du fanger det er op til dig; hvis fejlen fortsætter, sender jeg en e-mail og siger "gå og tjek databasen".

  • Tilslutning til databasen virker:

    Variablen db er tildelt i din try:... except blok. Hvis connect metoden virker så db er erstattet med forbindelsesobjektet.

Uanset hvad, startværdien af ​​db er aldrig brugt.

Jeg har dog hørt, at det er dårlig praksis at bruge undtagelseshåndtering til flowkontrol som denne.

I modsætning til andre sprog gør Python brug undtagelseshåndtering til flowkontrol. I slutningen af ​​mit svar har jeg linket til flere spørgsmål om Stack Overflow og programmører, der stiller et lignende spørgsmål. I hvert eksempel vil du se ordene "men i Python".

Det betyder ikke, at du skal gå overbord, men Python bruger generelt mantraet EAFP, "Det er lettere at bede om tilgivelse end tilladelse." De tre bedste eksempler i Hvordan kontrollerer jeg, om der findes en variabel? er gode eksempler på, hvordan du både kan bruge flowstyring eller ej.

Er indlejring af undtagelser en god idé? Eller er der en bedre måde at håndtere afhængige/kaskadede undtagelser som denne?

Der er intet galt med indlejrede undtagelser, igen, så længe du gør det fornuftigt. Overvej din kode. Du kan fjerne alle undtagelser og pakke det hele ind i et try:... except blok. Hvis en undtagelse er rejst, så ved du, hvad det var, men det er lidt sværere at spore præcis, hvad der gik galt.

Hvad sker der så, hvis du selv vil sige e-mail om fejlen i cursor.execute ? Du bør have en undtagelse omkring cursor.execute for at udføre denne ene opgave. Du re-raiser så undtagelsen, så den fanges i dit ydre try:... . Ikke re-raising ville resultere i, at din kode fortsætter, som om intet var hændt, og hvilken logik du end havde lagt i dit ydre try:... at håndtere en undtagelse ville blive ignoreret.

I sidste ende er alle undtagelser arvet fra BaseException .

Der er også nogle dele (f.eks. forbindelsesfejl), hvor jeg gerne vil have, at scriptet bare afsluttes - derfor det kommenterede sys.exit()-kald.

Jeg har tilføjet en simpel klasse, og hvordan man kalder den, hvilket er nogenlunde sådan, jeg ville gøre, hvad du prøver at gøre. Hvis dette skal køres i baggrunden, er udskrivningen af ​​fejlene ikke umagen værd - folk vil ikke sidde der manuelt og se efter fejl. De skal logges på, hvad end din standardmåde er, og de relevante personer skal underrettes. Jeg har fjernet udskriften af ​​denne grund og erstattet med en påmindelse om at logge.

Da jeg har opdelt klassen i flere funktioner, når connect metode mislykkes, og en undtagelse hæves i execute opkaldet vil ikke blive kørt, og scriptet afsluttes efter forsøg på at afbryde forbindelsen.

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

Så kald det:

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

Yderligere læsning:

cx_Oracle dokumentation

Hvorfor ikke bruge undtagelser som regelmæssig kontrol?
Er håndtering af python-undtagelser mere effektiv end PHP og/eller andre sprog?
Argumenter for eller imod at bruge try catch som logiske operatorer



  1. Den hurtigste måde at kontrollere, om nogle poster i en databasetabel?

  2. Nye og udviklende PostgreSQL Enterprise-funktioner med seneste udgivelser

  3. Hvordan nulstiller man kørende SUM, efter at den når en tærskel?

  4. Android ListView ved hjælp af SQLite