Connessione al database - dovrebbero essere passati come parametri?

9

Abbiamo un sistema in cui la connessione al database si ottiene una volta utilizzando un metodo comune e passando per tutta la classe pertinente da utilizzare. Ci sono dubbi sul fatto che il passaggio della connessione al database come parametro a classi diverse possa causare problemi, quindi sto verificando qui per vedere se questo è effettivamente fattibile, e ci sono dei modelli migliori per farlo?

So che ci sono alcuni strumenti ORM per fare la persistenza, ma non possiamo entrare in quello, ancora ..

Qualsiasi feedback è ben accetto, grazie.

    
posta ipohfly 14.02.2013 - 04:58
fonte

5 risposte

8

Sì, è sicuro passare una connessione. Gestisci la connessione in un blocco di controllo esterno. Non c'è nulla di pericoloso a riguardo.

Ciò che è pericoloso è la scrittura di codice che non garantisce che la connessione sia correttamente disposta in modo tempestivo. Dimenticare di ripulire una risorsa non è correlato alla sua diffusione. Potresti semplicemente scrivere codice che lascia una connessione sospesa senza passarla da nessuna parte.

In C ++, sei protetto da RAII se ti alleni sullo stack o usi puntatori intelligenti. In C # si impegna una regola che tutti gli oggetti usa e getta (come le connessioni) siano dichiarati in un blocco "using". In Java ripulire con logica try-finally. Avere recensioni di codice su tutto il codice del livello dati per garantire questo.

Il caso d'uso più comune è quando si hanno diverse operazioni che possono essere combinate in molte permutazioni. E ciascuna di queste permutazioni deve essere una transazione atomica (tutto successo o rollback). quindi devi passare la transazione (e quindi la connessione corrispondente) a tutti i metodi.

Supponiamo di avere molte azioni foobar () che possono essere combinate in vari modi come transazioni atomiche.

//example in C#
//outer controlling block handles clean up via scoping with "using" blocks.
using (IDbConnection conn = getConn())
{
    conn.Open();
    using (IDbTransaction tran = conn.BeginTransaction())
    {
        try
        {//inner foobar actions just do their thing. They don't need to clean up.
            foobar1(tran);
            foobar2(tran);
            foobar3(tran);
            tran.Commit();
        }
        catch (Exception ex)
        { tran.Rollback(); }
    }
}//connection is returned to the pool automatically

BTW vorrai aprire le connessioni il più tardi possibile, disponili al più presto possibile. I tuoi compagni di squadra potrebbero avere ragione se stai trattando le connessioni come membri degli oggetti, introducendoli come uno stato non necessario e lasciando le connessioni aperte molto più a lungo del necessario. Ma l'atto di passare una connessione o una transazione come parametro non è intrinsecamente sbagliato.

A proposito. In base al supporto della tua lingua per le funzioni di prima classe, puoi visualizzare un elenco di azioni foobar (). Quindi una funzione potrebbe gestire tutte le permutazioni delle azioni. Eliminazione della duplicazione del blocco di controllo esterno per ogni permutazione.

    
risposta data 14.02.2013 - 16:24
fonte
6

Sembra che tu stia cercando Iniezione di dipendenza . Cioè, la connessione in pool viene creata una volta e iniettata ovunque sia necessaria. Certamente passare la connessione tramite un parametro metodo è un modo per iniettare le dipendenze, ma un contenitore IoC come Guice, PicoContainer o Spring è un altro (più sicuro) modo in cui puoi farlo.

L'uso di DI significa che puoi racchiudere ordinatamente la logica intorno alla creazione, all'apertura, all'utilizzo e alla chiusura della connessione, lontano dalla tua logica aziendale di base.

Spring JDBC e altri sono altri esempi di questo tipo di comportamento per te

    
risposta data 14.02.2013 - 10:00
fonte
2

Passare attorno alle cose del database piuttosto che ai dati può portare a problemi. In tal senso, ogni volta che è pratico, non passare una cosa di database a meno che non si possa garantire un'adeguata igiene del database.

Il problema con il passaggio di cose del database è che può essere sciatta. Ho visto più di un bug nel codice con qualcuno che passa attorno ad una connessione al database, che qualcuno poi afferra un risultato impostato su e si arresta in un oggetto locale (il set di risultati, ancora connesso al database) e quindi lega un cursore nel database per un tempo significativo. Un'altra istanza qualcuno ha passato un set di risultati a qualcun altro (che è stato quindi nascosto) e quindi il metodo che ha passato il set di risultati lo ha chiuso (e l'istruzione) che ha causato errori quando altri metodi hanno provato a lavorare con il set di risultati che non era più.

Tutto ciò deriva dal non rispetto del database, della connessione, dell'istruzione, del set di risultati e dei loro cicli di vita.

Per evitarlo, ci sono schemi e strutture esistenti che giocano più bene con i database e non hanno le cose del database che devono uscire dalle classi in cui sono confinati. I dati entrano, i dati si spengono, il database rimane chiuso .

    
risposta data 14.02.2013 - 16:44
fonte
2

Il passaggio di Connection di istanze in giro non è di solito un problema, anche se nella maggior parte delle situazioni solo le implementazioni DAO dovrebbero avere nulla a che fare con esse. Ora, visto che il problema riguarda le connessioni che non vengono chiuse dopo l'uso, è in realtà facile da correggere: l'oggetto Connection deve essere chiuso allo stesso livello in cui è aperto, ovvero nello stesso metodo. Io personalmente utilizzo il seguente modello di codice:

final Connection cnx = dataSource.getConnection();
try {
    // Operations using the instance
} finally {
    cnx.close();
}

In questo modo mi assicuro che tutte le connessioni siano sempre chiuse, anche se viene generata un'eccezione all'interno del blocco. In realtà, continuo a utilizzare lo stesso identico pattern per Statement e ResultSet istanze, e finora tutto è andato a buon fine.

Modifica 2018-03-29: come indicato dall'utente1156544 nei commenti seguenti, a partire da Java 7 dovrebbe essere privilegiato l'uso del costrutto try-with-resources. Usandolo, il pattern di codice che ho fornito nella mia risposta iniziale può essere semplificato in questo modo:

try (final Connection cnx = dataSource.getConnection()) {
    // Operations using the instance
}
    
risposta data 14.02.2013 - 17:15
fonte
0

c'è un compromesso nel fare le cose in questo modo piuttosto che usare un singleton che puoi ottenere se necessario. Ho fatto le cose in entrambi i modi in passato.

In generale, è necessario pensare alle conseguenze della gestione della connessione al database e questo può o meno essere ortogonale all'uso della query del database. Ad esempio, se si dispone di una connessione db per una determinata istanza dell'applicazione e viene chiusa quando non in uso, ciò sarebbe ortogonale. Metti la gestione in una classe singleton e non passarla in giro. Questo ti permette di gestire la connessione db come ti serve. Ad esempio, se vuoi chiudere una connessione su ogni commit (e riaprire alla prossima chiamata) è più facile farlo su un singleton perché l'API per questo può essere centralizzata.

D'altra parte, supponiamo di dover gestire un pool di connessioni in cui una determinata chiamata potrebbe dover utilizzare qualsiasi connessione arbitraria. Questo potrebbe accadere quando si eseguono transazioni distribuite su più server, ad esempio. In questo caso, in genere, è molto meglio passare l'oggetto di connessione db piuttosto che lavorare con i singleton. Penso che questo sia di solito il caso più raro, ma non c'è niente di sbagliato nel farlo quando è necessario.

    
risposta data 23.02.2013 - 06:10
fonte

Leggi altre domande sui tag