2009-03-01 20 views
9

Folks,orientato agli oggetti, come le strutture di database relazionali

Per il momento n-esima di fila, mi colpisce di nuovo lo stesso vecchio problema. Si tratta di "come mappare le strutture OOP alle tabelle del database in modo indolore".

Ecco uno scenario: nel mio sistema ho diversi tipi di "attori": lavoratori, datori di lavoro, contatti. Hanno alcune funzionalità in comune; altri pezzi sono molto diversi. Le entità con cui tutti gli attori si occupano sono "comunicazioni", "note" (gli amministratori amano lasciare note sui clienti) e altre ancora. Ci sono un sacco di tipi di altre entità di cui ogni tipo di attore si occupa, mentre gli altri no.

Attualmente, il mio schema del database comprende tabelle per:

Attori:

  • lavoratori
  • datore di lavoro
  • contatto

Entità:

  • comunicazione
  • note
  • ecc

tabelle di associazione tra enti e attori:

  • lavoratore-comunicazione-Assn
  • datore di lavoro-comunicazione-Assn
  • lavoratori-notes -assn
  • ecc., ottieni il drillo l.

Questo mi sembra un "odore di codice". Ogni volta che un cliente cambia il proprio ruolo (cioè promosso da "contatto" a "datore di lavoro"), è necessario eseguire una serie di script pazzi. Yuck ... D'altra parte, se stavo operando in un mondo puramente orientato all'OOP, sarebbe molto più semplice - avere una classe base per tutte le entità con proprietà comuni, ed essere fatto con esso ...

Nel mondo DB, questa opzione sembra teoricamente possibile, ma suona molto confusa ... Cioè se capisco bene, avrei una nuova tabella base_actor, e ogni altro attore avrebbe un base_actor_id, e quindi le associazioni sarebbero tra base_actor e le entità ... Ma allora, come faccio le query di associazione inversa? Cioè "Mostrami tutte le comunicazioni con solo attori di tipo operaio"?

Qualche consiglio? Qualche idea generale sull'argomento "mappare le strutture OOP al DB relazionale"?

risposta

6

Ecco una soluzione che ho trovato circa 10 anni fa. Il sistema che utilizza questo design è ancora in esecuzione, quindi ha funzionato abbastanza bene per sopravvivere più a lungo della maggior parte del mio codice. ;) Oggi potrei usare uno dei pacchetti ORM che Scott menziona, ma non ci sono davvero grossi problemi semplicemente usando SQL direttamente.

  1. Modella tutte le relazioni di ereditarietà come join tra tabelle. Ogni tabella nel sistema terrà gli attributi di una classe specifica.

  2. Utilizzare un oggetto sintetico id (oid) come chiave primaria per tutti gli oggetti. Un generatore di sequenze o una colonna di autoincremento è necessario per generare valori di oid.

  3. Tutte le classi ereditate devono utilizzare lo stesso tipo di oid del genitore. Definisci l'oid come chiave esterna con eliminazione in cascata. La tabella genitore ottiene la colonna di autoincrement oid e i bambini ottengono semplici colonne di oid.

  4. Le query sulle classi finali vengono eseguite nella tabella corrispondente. Puoi unire tutte le tabelle della classe genitore alla query o solo pigro caricare gli attributi che ti servono. Se la gerarchia dell'ereditarietà è profonda e si hanno molte classi, un pacchetto ORM può davvero semplificare il codice. Il mio sistema aveva meno di 50 classi con una profondità di ereditarietà massima di 3.

  5. Le query tra classi figlio (ad es. Query su una classe padre) possono caricare pigro gli attributi figlio per ogni istanza oppure è possibile ripetere la query per ogni classe secondaria unita alle classi base. Gli attributi figlio di caricamento lento basati su una query della classe genitore richiedono la conoscenza del tipo di oggetto. Potresti avere già abbastanza informazioni nelle classi genitore, ma se non hai bisogno di aggiungere informazioni sul tipo. Ancora una volta, questo è dove un pacchetto ORM può aiutare.

classi virtuali senza attributi membri possono essere saltati nella struttura della tabella, ma non sarà in grado di interrogare sulla base di tali classi.

Ecco come appare "Mostrami tutte le comunicazioni con gli attori di tipo operaio".

select * from comm c, worker w where c.actor=w.oid; 

Se si dispone di sottoclassi di comunicazione, e si desidera caricare immediatamente tutti gli attributi della classe bambino (forse il vostro sistema non consente la costruzione parziale), la soluzione più semplice è quella di desiderosi di unirsi a tutte le possibili classi .

select * from comm c, worker w, missive m where c.actor=w.oid and c.oid=m.oid; 
select * from comm c, worker w, shoutout s where c.actor=w.oid and c.oid=s.oid; 

Un'ultima cosa. Assicurati di avere un buon database e gli indici corretti. Le prestazioni possono essere un problema serio se il database non è in grado di ottimizzare questi join.

+0

grazie per una risposta esauriente! Penso che mi stia prendendo in giro - molte delle risposte suggeriscono un simile approccio di "partizionamento" delle chiavi primarie usando questo approccio "chiave primaria del bambino = chiave estranea in genitore". Ha molto senso. –

+0

http://martinfowler.com/eaaCatalog/concreteTableInheritance.html – troelskn

3

L'appello che suggerisci mi sembra giustificato. Puoi aggiungere una colonna actortype alla tabella base-attore per differenziare tra diversi tipi di attori. Il PK di ciascuna tabella di attori specifici sarebbe un FK per la tabella actorbase per evitare query "pelose" e per emulare la relazione "is-a" simile all'ereditarietà.

+0

Opere in qualche modo, ad eccezione che la query associazione inversa diventa peloso: "Mi mostrano i nomi di tutti i lavoratori che ho inviato comunicazioni nell'ultimo mese" –

+0

selezionare i nomi da comunicazione c inner join actor_base ab su c.actorid = ab.actorid dove ab.actortype = 'worker' – Manu

+0

e c.date between getdate() e dateadd (m, -1, getdtate()) – Manu

7

Si chiama ORM or Object Relational Mapping. Ci sono dozzine di prodotti che si propongono di aiutarti a mappare le strutture OO alle tabelle relazionali. Ruby on Rails, ad esempio, offre Active Record per contribuire a colmare il divario.Per PHP hai Propel e Doctrine e Porte e molti altri.

+0

Sono molto familiare con ORM; Sto usando il framework QCodo, che offre una buona capacità di mappatura da oggetto a tavolo, ma non va bene per questo particolare compito. Propel o Doctrine è meglio in questo compito specifico? –

2

la risposta migliore che abbia mai visto per questo è stato: http://en.wikipedia.org/wiki/The_Third_Manifesto

Purtroppo non è qualcosa che si inserisce nello spazio di una sola risposta qui su StackOverflow. Cercherò di abbreviare qui, ma ti avverto che tale abbreviazione non sarà un riflesso accurato del terzo manifesto. Si prega di reindirizzare tutte le critiche di questa soluzione per leggere effettivamente quella dannata cosa, invece di presumere di averlo compreso completamente leggendo l'abbreviazione. Ok, ecco qui.

definire tre nuovi tipi di colonna denominati worker, employer e contact. Memorizza oggetti di ciascuno di questi tipi, in colonne dei rispettivi tipi. Segui le regole standard di normalizzazione per il resto del tuo modello di dati.

La mia sensazione è che l'attuale tecnologia di database popolare in realtà non supporta il modo "corretto" per fare queste cose, (in particolare, molti sistemi di database non consentono la definizione di nuovi tipi). quindi non importa quello che fai, sarai sempre costretto a una situazione di compromesso. Ma dopo aver letto il terzo manifesto, almeno saprai su cosa stai compromettendo.

Attualmente ORM è la soluzione più diffusa del problema al momento, ma non credo che sia la soluzione corretta.

8

.. Si tratta di "come mappare le strutture OOP alle tabelle del database in modo indolore".

Non è così.

L'algebra orientata agli oggetti e relazionale sono due paradigmi fondamentalmente diversi. Non è possibile la transizione tra di loro senza un'interpretazione soggettiva. Questo è chiamato disadattamento di impedenza ed è stato soprannominato The Vietnam of Computer Science.

+0

Punto preso, ma sei estremo. Se hai un sistema di battitura dinamico (come Javascript o Python) è facile caricare parzialmente una classe e poi completare il carico pigramente. Nella mia esperienza, il problema è in teoria peggiore che nella pratica. –

+0

Forse sto esagerando un po ', ma la domanda per me sembrava "Qual è il proiettile d'argento" - E non ce n'è uno. Il caricamento lento è abbastanza buono, ma il vero problema è nelle relazioni tra entità. Per casi di uso semplice, funziona bene, ma i grafici di oggetti complessi semplicemente non si adattano bene. – troelskn

0

Per me sembra proprio che al modello di dati manchi un livello. Vorrei configurarlo più simile a questo:

persone Tabella - (informazioni Quasi le persone reali)

Ruoli Table - (I tipi di ruoli le persone possono avere cioè dei lavoratori, datore di lavoro, Contatto - e le informazioni specifiche per quel ruolo)

PeopleRoles Table - (people_id, ROLE_ID, magari iniziare/modificare le date, ecc)

Entità Tabella - (Definire i diversi tipi di entità)

RoleEntities Tabella - (ROLE_ID, entity_id, ecc)

Poi cambiare una persona da un ruolo all'altro (o permettere loro di avere molteplici ruoli) è un semplice aggiornamento.

3

Quello che stai cercando è Disjoint-subtypes ... ORM è un hack.

+0

Grazie mille per aver trovato quell'altro thread. Molto utile. –

0

Molti RDBMS offrono una funzione di ereditarietà della tabella, che collega le tabelle padre alle tabelle figlio nello stesso modo dell'ereditarietà delle classi.l'implementazione varia un po 'da un fornitore all'altro, ma può richiedere un po' di fatica dall'implementazione di concetti simili.

Inoltre, la maggior parte degli RDBMS presenta una combinazione di trigger, viste memorizzate e stored procedure che possono separare il comportamento dall'implementazione. In molti casi, come le regole di PostgreSQL (una generalizzazione delle viste) offrono un incapsulamento molto sofisticato e sono abbastanza facili da usare.

0

Un paio di persone hanno notato il disadattamento di impedenza relazionale dell'oggetto. La soluzione migliore è semplicemente rinunciare allo RDBMS in favore dello OODBMS, che ha recentemente riacquistato popolarità.

Detto questo, per quanto ne so, non ci sono database di oggetti con API in PHP puro. Una ricerca rapida ha prodotto this result ma non è stato aggiornato da anni. D'altra parte, ho sentito parlare di numerosi database di oggetti per altre lingue, tra cui Hibernate, db4o e ZODB.

+0

Pensavo che gli OODBMS fossero morti sul retro mentre tutti continuavano a usare RDBMS (o ORDBMSs, come a Oracle piace chiamarsi ora). –

1

Probabilmente vale la pena dedicare tempo alla familiarizzazione con la modellazione dei ruoli degli oggetti come discusso in this question. Il problema più grande che vedo è che non esiste una metodologia accettata per avere una discussione sul design concettuale sui dati relazionali. Il meglio che puoi fare è la modellazione logica (di solito gli ERM). La modellazione dei ruoli degli oggetti fornisce le basi per tale discussione. Spero che vedrete artefatti riconoscibili da una discussione sul design OOP simile che potreste avere.

Problemi correlati