Quando dovrei usare DBIx :: Class di Perl?

17

DBIx :: Class è una popolare interfaccia Perl per qualsiasi database a cui è possibile connettersi tramite DBI . C'è una buona documentazione per i suoi dettagli tecnici, ma poche informazioni sul suo uso corretto (comprese le situazioni in cui probabilmente non lo vuoi).

In molte situazioni, le persone lo raggiungono in modo riflessivo perché pensano che dovrebbero usarlo per tutto ciò che riguarda un database. Tuttavia, l'ho visto più spesso abusato fino al punto in cui diventa un punto dolente. La mia domanda durante le revisioni del codice e dell'architettura è sempre "Quale beneficio offre Foo?" Molto spesso, gli sviluppatori che vedo in queste situazioni non possono dare una risposta coerente a questo. Ma poi, spesso non capiscono il semplice SQL.

Negli ultimi due mesi ho chiesto alle persone "Perché usi DBIx :: Class?" e ho ricevuto solo una buona risposta (e anche quella persona è stata in grado di rispondere al follow-up "Quando non la useresti"). Peter Rabbitson, lo sviluppatore principale, si è avvicinato a una risposta nella sua intervista su FLOSS Weekly , ma è un un po 'seppellito nel mezzo dell'intervista.

Quindi, come posso decidere se usare DBIx :: Class è appropriato per un progetto?

    
posta brian d foy 08.12.2015 - 03:22
fonte

7 risposte

23

Prima di rispondere alla domanda, penso che sia di base uno sfondo.

Il nucleo del problema

Dopo anni di interviste e assunzione di sviluppatori, ho imparato due cose:

  1. La stragrande maggioranza degli sviluppatori ha poca esperienza in progettazione del database.

  2. Ho notato una correlazione allentata tra coloro che non capiscono database e coloro che odiano gli ORM.

(Nota: e sì, so che ci sono quelli che capiscono molto bene i database che odiano gli ORM)

Quando le persone non capiscono perché le chiavi esterne sono importanti, perché non incorporare il nome del produttore nella tabella item , o perché customer.address1 , customer.address2 e customer.address3 campi non sono una buona idea, l'aggiunta di un ORM per rendere più facile per loro scrivere bug del database non aiuterà nulla.

Invece, con un database progettato correttamente e un caso di utilizzo OLTP, gli ORM sono d'oro. Gran parte del lavoro del grunt va via e con strumenti come DBIx :: Class :: Schema :: Loader , posso passare da un buon schema di database a codice Perl funzionante in pochi minuti. Vorrei citare la regola di Pareto e dire che l'80% dei miei problemi sono stati risolti con il 20% del lavoro, ma in realtà, trovo i benefici ancora più grandi di quello.

Abuso della soluzione

Un altro motivo per cui alcune persone odiano gli ORM è perché lasceranno perdere l'astrazione. Consideriamo il caso comune delle app Web MVC. Ecco qualcosa che comunemente vediamo (pseudo-codice):

GET '/countries/offices/$company' => sub {
    my ( $app, $company_slug ) = @_;
    my $company = $app->model('Company')->find({ slug => $company_slug }) 
      or $app->redirect('/');
    my $countries = $app->model('Countries')->search(
     {
         'company.company_id' => $company->company_id,
     },
     {
         join     => [ offices => 'company' ],
         order_by => 'me.name',
     },
   );
   $app->stash({
     company   => $company,
     countries => $country,
   });
}

Le persone scrivono percorsi di controller del genere e si accarezzano le spalle, pensando che sia un codice buono e pulito. Sarebbero inorriditi con SQL hard-coding nei loro controller, ma hanno fatto poco più che esporre una sintassi SQL diversa. Il loro codice ORM deve essere inserito in un modello e quindi possono farlo:

GET '/countries/offices/$company' => sub {
   my ( $app, $company_slug ) = @_;
   my $result = $app->model('Company')->countries($company_slug)
     or $app->redirect('/');
   $app->stash({ result => $result });
}

Sai cosa è successo ora? Hai incapsulato correttamente il tuo modello, non hai esposto l'ORM e, successivamente, quando trovi che puoi recuperare i dati da una cache anziché dal database, non è necessario modificare il codice del controller (ed è più semplice scrivere test per questo e riutilizzare la logica).

In realtà, ciò che accade è che le persone perdono il loro codice ORM su tutti i loro controllori (e viste) e quando hanno problemi di scalabilità, iniziano a incolpare l'ORM piuttosto che la loro architettura. L'ORM subisce un brutto colpo (lo vedo ripetutamente per molti clienti). Invece, nascondi quell'astrazione in modo che quando hai effettivamente raggiunto i limiti ORM, puoi scegliere le soluzioni appropriate per il tuo problema piuttosto che lasciare che il codice sia così strettamente associato all'ORM che sei legato al maiale.

Rapporti e altri limiti

Come Rob Kinyon ha chiarito in precedenza, la segnalazione tende ad essere un punto debole negli ORM. Questo è un sottoinsieme di un problema più ampio in cui SQL o SQL complessi che si estendono su più tabelle a volte non funzionano bene con gli ORM. Ad esempio, a volte l'ORM forza un tipo di join che non desidero e non posso dire come risolverlo. O forse voglio usare un suggerimento indice in MySQL, ma non è facile . O a volte l'SQL è così complicato che sarebbe più carino scrivere SQL piuttosto che l'astrazione fornita.

Questo è uno dei motivi per cui ho iniziato a scrivere DBIx :: Class :: Report . Finora funziona bene e risolve la maggior parte dei problemi che le persone hanno qui (a patto che stiano bene con un'interfaccia di sola lettura). E mentre sembra come una stampella, in realtà, fintanto che non stai perdendo la tua astrazione (come spiegato nella sezione precedente), rende ancora più facile lavorare con DBIx::Class .

Quindi quando dovrei scegliere DBIx :: Class?

Per me, lo sceglierei la maggior parte delle volte in cui ho bisogno di un'interfaccia per un database. L'ho utilizzato per anni. Tuttavia, potrei non sceglierlo per un sistema OLAP, e i programmatori più recenti avranno certamente difficoltà a farlo. Inoltre, trovo spesso che ho bisogno di meta-programmazione e mentre DBIx::Class fornisce gli strumenti, sono molto scarsamente documentati.

La chiave per usare correttamente DBIx::Class è la stessa della maggior parte degli ORM:

  1. Non perdere l'astrazione.

  2. Scrivi i tuoi dannati test.

  3. Sapere come passare a SQL, se necessario.

  4. Scopri come normalizzare un database.

DBIx::Class , una volta appresa, si prenderà cura della maggior parte del tuo lavoro pesante per te e renderà più semplice scrivere rapidamente le applicazioni.

    
risposta data 08.12.2015 - 11:45
fonte
9

Per sapere quando usare qualcosa, è importante capire qual è lo scopo della cosa. Qual è l'obiettivo del prodotto.

DBIx :: Class è un ORM - Object-Relational Mapper. Un ORM prende le strutture di dati del database relazionale basato su Relational / Set e lo mappa su un albero Object. La mappatura tradizionale è un oggetto per riga, usando la struttura della tabella come descrizione di una classe. Le relazioni padre-figlio nel database sono considerate come relazioni di contenimento tra oggetti.

Ecco le sue ossa. Ma questo non ti aiuta a decidere se dovresti usare un ORM. Gli ORM sono principalmente utili quando si verificano le seguenti condizioni:

  • Si utilizza un database relazionale.
  • I tuoi dati sono ampiamente utilizzati per OLTP (Online Transaction Processing).
  • Non scrivi rapporti all'interno della tua applicazione.

Il più grande punto di forza di un ORM è la costruzione di un buon SQL per camminare su un grafico ad albero sovrapposto alla struttura dei dati relazionali. L'SQL è spesso peloso e complesso, ma questo è il prezzo per gestire la mancata corrispondenza dell'impedenza.

Mentre gli ORM sono molto bravi a scrivere SQL per l'estrazione di righe, sono molto scarsi nella scrittura di collisioni SQL. Questo è il tipo di SQL su cui sono basati i report. Questo tipo di regole di confronto viene creato utilizzando diversi strumenti, non un ORM.

Ci sono molti ORM in varie lingue, diversi in Perl. Altri mappatori sono Class :: DBI e Rose :: DB. DBIx :: Class è spesso considerato migliore degli altri in gran parte sulla base dei suoi risultati. Questo è un concetto in cui la generazione SQL è separata dall'esecuzione SQL.

Aggiornamento : in risposta a Ovid, DBIx :: Class (tramite SQL :: Abstract) offre la possibilità di specificare sia le colonne da restituire che i suggerimenti dell'indice da utilizzare.

In generale, tuttavia, se si desidera eseguire questa operazione, è meglio non utilizzare un ORM per quella specifica query. Ricorda: lo scopo principale di un ORM è di mappare le righe in una tabella agli oggetti di una classe i cui attributi sono le colonne della tabella. Se stai compilando solo alcuni attributi, i potenziali utenti di quell'oggetto non sapranno quali attributi sono popolati o meno. Ciò porta a un'orribile programmazione difensiva e / oa un odio generale nei confronti degli ORM.

Quasi sempre, il desiderio di utilizzare suggerimenti sugli indici o di limitare le colonne restituite è un'ottimizzazione per la velocità e / o una query di aggregazione.

  • Le query di aggregazione sono gli OR di caso d'uso per cui NON è stato progettato. Mentre DBIx :: Class può creare query aggregate, non stai creando un grafico oggetto, quindi usa DBI direttamente.
  • Vengono utilizzate le ottimizzazioni delle prestazioni perché i dati interrogati sono troppo grandi per il database sottostante, indipendentemente da come lo si accede. La maggior parte dei database relazionali è ideale per le tabelle con righe fino a 1-3MM che escono da SSD in cui la maggior parte degli indici dati + si adatta alla RAM. Se la tua situazione è più grande di questa, allora ogni database relazionale avrà problemi.

Sì, un ottimo DBA può rendere le tabelle con le righe 100MM + in Oracle o SQL * Server. Se stai leggendo questo, non hai un ottimo DBA sul personale.

Tutto ciò detto, un buon ORM non si limita a creare grafici di oggetti, ma fornisce anche una definizione introspecibile del database. Puoi usare questo per aiutare a creare query ad-hoc e consumarle come faresti con DBI, senza creare il grafico dell'oggetto.

    
risposta data 08.12.2015 - 04:07
fonte
8

Essendo uno degli sviluppatori principali della piattaforma di e-commerce Interchange6 (e la motosega principale dello schema) ho un'esperienza abbastanza approfondita con DBIC. Ecco alcune delle cose che lo rendono una piattaforma eccezionale:

  • Il generatore di query consente di scrivere una volta per molti motori di database (e più versioni di ciascuno). Attualmente supportiamo Interchange6 con MySQL, PostgreSQL e SQLite e aggiungeremo il supporto per più motori una volta ottenuti il tempo e le risorse. Ci sono attualmente solo due percorsi di codice nell'intero progetto che hanno un codice aggiuntivo per soddisfare le differenze tra i motori e questo è dovuto semplicemente alla mancanza di una funzione specifica del database (SQLite è carente in alcuni punti) oa causa dell'idiozia di MySQL che ha cambiato il modo in cui la sua funzione LEAST gestisce NULL tra due versioni minori.

  • Le query predefinite significano che posso creare semplici metodi che possono essere chiamati (con o senza argomenti) dal codice dell'applicazione, quindi tengo le mie query principalmente all'interno della definizione dello schema invece di sporcare il mio codice dell'applicazione.

  • La generazione di query composable consente di suddividere le query in piccole query predefinite comprensibili e quindi concatenate per creare query complesse che sarebbero difficili da mantenere a lungo termine in DBIC (anche peggio in SQL puro) se fossero costruito in un unico passaggio.

  • Schema :: Loader ci ha permesso di utilizzare DBIC con applicazioni legacy dando una nuova prospettiva di vita e un percorso molto più semplice per il futuro.

  • Plugin DBIC, DeploymentHandler e amp; Le migrazioni si aggiungono immensamente all'insieme di strumenti che semplificano la vita.

Una delle enormi differenze tra DBIC e la maggior parte delle piattaforme simili a ORM / ORM è che sebbene cerchi di guidarti nel suo modo di fare le cose, non ti impedisce di fare cose pazzesche che ti piacciono:

  • È possibile utilizzare le funzioni SQL e le stored procedure che DBIC non conosce solo fornendo il nome della funzione come chiave nella query (può anche risultare divertente quando si tenta di utilizzare LEAST in MySQL ^^ ma che non è colpa di DBIC).

  • SQL letterale può essere utilizzato quando non esiste un 'metodo DBIC' per fare qualcosa e il risultato restituito è ancora racchiuso in classi piacevoli con accessor.

TL; DR Probabilmente non mi preoccuperei di usarlo per applicazioni davvero semplici con solo un paio di tavoli, ma quando devo gestire qualcosa di più complesso, specialmente dove la compatibilità tra motori e la manutenzione a lungo termine sono fondamentali, quindi DBIC è generalmente il mio percorso preferito.

    
risposta data 08.12.2015 - 11:47
fonte
7

(Prima di iniziare dovrei dire che questo confronta solo i wrapper DBI con DBIC, DBI e Mojo. Non ho esperienza con nessun altro ORM di Perl e quindi non commenterò direttamente su di essi).

DBIC fa molte cose molto bene. Non ne sono un grande utilizzatore, ma conosco la teoria. Fa un buon lavoro di generazione SQL e in particolare (come mi è stato detto) maneggiando i join ecc. Può anche fare molto bene il prefetching di altri dati correlati.

Il principale vantaggio, a mio avviso, è la possibilità di utilizzare DIRECTLY i risultati come classe del modello. Questo è altrimenti noto come "l'aggiunta di metodi di set di risultati" in cui è possibile ottenere i risultati e chiamare i metodi su tali risultati. L'esempio comune è il recupero di un oggetto utente da DBIC e quindi la chiamata di un metodo per verificare se la loro password è valida.

La distribuzione sicura degli schemi può essere difficile, ma è sempre difficile. DBIC ha strumenti (alcuni in moduli esterni) che rendono più facile e probabilmente più semplice che gestire i propri schemi a mano.

Dall'altra parte della medaglia, ci sono altri strumenti che fanno appello ad altre sensibilità, come i wrapper DBI aromatizzati al mojo. Questi hanno il fascino di essere magra e tuttavia ancora utilizzabile. Molti hanno anche preso spunto da Mojo :: Pg (l'originale) e aggiunto funzionalità utili come la gestione dello schema in file flat e l'integrazione di pubsub.

Questi moduli aromatizzati con Mojo sono nati da un altro punto debole del DBIC, ovvero che non è (ancora) in grado di eseguire query asincrone. Gli autori mi hanno assicurato che è tecnicamente possibile forse anche in fretta, ma ci sono problemi nella progettazione di una API che sarebbe adatta. (A dire il vero, mi è stato persino chiesto di aiutarmi, e mentre lo facevo, non so come spostare l'ago nel tempo che devo dedicare ad esso).

TL; DR usa DBIC a meno che tu non ami SQL o tu abbia bisogno di async, nel qual caso investiga i wrapper DBI aromatizzati con Mojo.

    
risposta data 08.12.2015 - 05:35
fonte
6

Ho scritto i miei pensieri su questo in DBIC vs DBI tre anni fa. Per riassumere, ho elencato due ragioni principali:

  1. DBIC significa che non devo pensare a tutto l'SQL banale che è necessario per quasi tutte le applicazioni che scrivo.
  2. DBIC mi restituisce oggetti dal database anziché strutture di dati stupide. Ciò significa che ho tutta la bontà OO standard con cui giocare. In particolare, trovo molto utile poter aggiungere metodi ai miei oggetti DBIC.

Per quanto riguarda gli anti-pattern, l'unica cosa a cui posso pensare è la performance. Se vuoi veramente spremere ogni ciclo di clock dalla tua CPU, allora forse DBIC non è lo strumento giusto per il lavoro. Ma, certamente per le applicazioni che scrivono, quei casi sono sempre più rari. Non riesco a ricordare l'ultima volta che ho scritto una nuova applicazione che ha parlato con un database e non ha usato DBIC. Ovviamente, è utile se conosci un po 'di ottimizzazione delle query generate da DBIC.

    
risposta data 08.12.2015 - 09:56
fonte
0

Non sono un esperto Perl, ma lo uso molto. Ci sono molte cose che non so o so fare meglio; alcune cose che non sono ancora in grado di capire, nonostante la documentazione.

Tendo ad iniziare con DBI perché penso, "Oh, questo è un progetto semplice, non ho bisogno del gonfiarsi di un ORM e non voglio complicarmi con i moduli di configurazione e Schema." Ma molto rapidamente - quasi ogni volta - inizio rapidamente a maledirmi per quella decisione. Quando voglio iniziare a diventare creativo nelle mie query SQL (query dinamiche, non solo segnaposto di confronto), faccio fatica a mantenere la sanità mentale usando DBI. SQL :: Abstract aiuta molto, e in genere è probabilmente sufficiente per me. Ma la mia prossima lotta mentale è mantenere così tanto SQL nel mio codice. Mi distrae molto avere linee e righe di SQL incorporato all'interno di brutti heredoc. Forse ho bisogno di usare un IDE di qualità.

Alla fine, più volte che no, rimango con DBI dritto. Ma continuo a desiderare che ci fosse un modo migliore. DBIx :: Class ha alcune caratteristiche davvero belle e l'ho usato un paio di volte, ma sembra davvero eccessivo per tutti tranne i più grandi progetti. Non sono nemmeno sicuro di cosa trovo più complicato da gestire: DBI con SQL o DBIC sparsi con moduli Schema sparsi.

(Oh, roba come i vincoli e i trigger è un vantaggio enorme per me per DBIC.)

    
risposta data 08.12.2015 - 04:16
fonte
0

Il modo in cui lo faccio scala:

  1. crea una classe che fornisce il costruttore di socket DBI ei metodi di test.

  2. deriva tale classe nelle tue classi di query SQL (una classe per tabella sql) e verifica il socket al momento del costruttore.

  3. usa le variabili con scope class per contenere il nome della tabella e i nomi delle colonne dell'indice primario.

  4. Scrivi tutto il nome della tabella di interpolazione SQL e la colonna dell'indice primario da quelle variabili invece di definirle staticamente in SQL.

  5. usa i macro degli editor per permetterti di creare coppie di metodi DBI di base (preparare ed eseguire) mentre scrivi SOLO l'istruzione sql.

Se puoi farlo, puoi scrivere codice API pulito in cima al DBI tutto il giorno con relativa facilità.

Quello che troverai è che molte delle tue query saranno trasferibili su più tavoli. A quel punto è possibile tagliare e incollare in una classe EXPORTER e cospargerli nuovamente dove necessario. È qui che entra in gioco l'interpolazione con scope class del nome della tabella e dei nomi delle colonne dell'indice primario.

Ho utilizzato questo approccio per ridimensionare a centinaia di metodi DBI con prestazioni relativamente buone. Non vorrei provare a mantenere il codice DBI in un altro modo.

Quando usare il DBI: sempre.

Non penso che quella fosse la tua vera domanda. La tua vera domanda era: "Questo sembra un enorme PITA, per favore dimmi che non devo farlo?"

Non lo fai. Sistemalo correttamente e la parte DBI diventa ridondante abbastanza da essere in grado di automatizzarla per lo più.

    
risposta data 19.07.2016 - 03:03
fonte

Leggi altre domande sui tag