Concetto: Database relazionali e orientamento oggetti
Questo concetto fornisce una panoramica dei modelli di oggetto e di dati relazionali e fornisce una descrizione di riepilogo di un framework di persistenza.
Relazioni
Descrizione principale

Introduzione

Questo documento concetto fornisce una panoramica dei modelli di oggetto e di dati relazionali e fornisce una descrizione di riepilogo di un framework di persistenza.

Database relazionali e orientamento oggetti

I database relazionali e l'orientamento oggetti non sono completamente compatibili. Essi rappresentano due differenti viste del mondo: in RDBMS, vengono visualizzati solo dati; in un sistema OO (object-oriented), viene visualizzata solo la funzionalità. Nessuna di queste due prospettive è migliore dell'altra: il modello OO (object-oriented) tende a funzionare bene per sistemi con funzionalità complessa e specifica dello stato in cui i dati sono secondari o in sistemi in cui l'accesso ai dati avviene tramite navigazione in una gerarchia naturale (ad esempio, elenchi di materiali). Il modello RDBMS è adatto per la creazione di report delle applicazioni e di sistemi in cui le relazioni sono dinamiche o ad-hoc.

Ciò che conta è che molte informazioni vengono memorizzate nei database relazionali e, se le applicazioni OO (object-oriented) desiderano accedere a tali dati, necessitano delle autorizzazioni alla lettura e scrittura a un RDBMS. Inoltre, i sistemi OO (object-oriented) spesso necessitano la condivisione dei dati con sistemi non OO (object-oriented). E' quindi naturale utilizzare un RDBMS come meccanismo di condivisione.

Sebbene la progettazione relazionale e quella OO (object-oriented) condividano alcune caratteristiche comuni (l'attributo di un oggetto è concettualmente simile a una colonna di entità), differenze fondamentali rendono una sfida un'integrazione priva di interruzioni. La differenza fondamentale è che i modelli di dati visualizzano i dati (tramite valori di colonna), mentre i modelli di oggetti li nascondono (racchiudendoli dietro le relative interfacce pubbliche).

Modello di dati relazionale

Il modello relazionale è composto da entità e relazioni. Un'entità può essere una tabella fisica o una proiezione di diverse tabelle, note anche come vista. La figura seguente illustra le tabelle LINEITEM, ORDER e PRODUCT e le rispettive relazioni. Un modello relazione presenta i seguenti elementi:

Il diagramma è descritto nel contenuto.

Modello relazionale

Un'entità contiene delle colonne. Ogni colonna è identificata da un nome e da un tipo. Nella figura precedente, l'entità LINEITEM contiene le colonne LineItem_Id (la chiave primaria), Description, Price, Quantity, Product_Id e Order_Id (le ultime due sono chiavi esterne che collegano l'entità LINEITEM alle entità ORDER e PRODUCT).

Un'entità contiene record o righe. Ogni riga rappresenta una serie univoca di informazioni che, generalmente, rappresentano i dati permanenti di un oggetto. 

Ogni entità contiene una o più chiavi primarie. LineItem_Id è la chiave principale per LINEITEM.

Il supporto per le relazioni è specifico del fornitore. L'esempio illustra il modello logico e la relazione tra le tabelle PRODUCT e LINEITEM. Nel modello fisico, le relazioni vengono generalmente implementate utilizzando i riferimenti chiave esterna / chiave primaria. Se un'entità è correlata a un'altra, essa conterrà colonne che sono chiavi esterne. Le colonne di chiavi esterne contengono dati che possono correlare specifici record nell'entità all'entità correlata.

Le relazioni presentano una molteplicità (nota anche come cardinalità). Le cardinalità comuni sono uno a uno (1:1), uno a molti (1:m), molti a uno (m:1) e molti a molti (m:n). Nell'esempio, LINEITEM presenta una relazione 1:1 con PRODUCT e PRODUCT presenta una relazione 0:m con LINEITEM.

Modello di oggetto

Un modello di oggetto contiene, tra gli altri elementi, classi (consultare [UML01] per una definizione completa di un modello di oggetto). Le classi definiscono la struttura e la funzionalità di una serie di oggetti, talvolta denominati istanze di oggetti. La struttura è rappresentata come attributi (valori di dati) e associazioni (relazioni tra classi). La seguente figura illustra un semplice diagramma di classe, visualizzando solo gli attributi (dati) delle classi.

Il diagramma è descritto nel contenuto.

Modello di oggetto (Diagramma della classe)

Un ordine dispone di un numero (il numero di ordine) e di un'associazione a 1 o più (1..*) voci di riga. Ogni voce di riga presenta una quantità (la quantità ordinata).

Il modello di oggetto supporta l'ereditarietà. Una classe può ereditare i dati e la funzionalità da un'altra classe (ad esempio, i prodotti SoftwareProduct e HardwareProduct ereditano gli attributi e i metodi dalla classe Product).

Framework di persistenza

La maggior parte delle applicazioni business utilizza la tecnologia relazionale come archivio dati fisico. La sfida per gli sviluppatori delle applicazioni OO (object-oriented) è di separare e incapsulare il database relazionale in modo tale che le modifiche nel modello di dati non interrompano il modello di oggetto e viceversa. Esistono numerose soluzioni che consentono alle applicazioni di accedere direttamente ai dati relazionali; la sfida è raggiungere un'integrazione senza interruzioni tra il modello di oggetto e il modello di dati.

Le API (application programming interface) del database vengono fornite con caratteristiche standard (ad esempio API Open Data Base Connectivity di Microsoft o ODBC) e sono proprietarie (collegamenti nativi a database specifici). Le API forniscono dei servizi di passaggio DML (data manipulation language) che consentono alle applicazioni di accedere ai dati relazionali non elaborati. In applicazioni OO (object-oriented), i dati devono essere sottoposti a una traduzione relazionale degli oggetti prima di potere essere utilizzati dall'applicazione. Ciò richiede una considerevole quantità di codice dell'applicazione per tradurre i risultati non elaborati dell'API del database API in oggetti dell'applicazione. Lo scopo del framework relazionale degli oggetti è incapsulare in modo generico l'archivio dati fisico e fornire i servizi di traduzione oggetti appropriati.

Il diagramma è descritto nel contenuto.

Scopo di un framework di persistenza

Gli sviluppatori dell'applicazione impiegano oltre il 30% del proprio tempo nell'implementazione dell'accesso al database relazionale in applicazioni OO (object-oriented). Se l'interfaccia relazionale degli oggetti non viene implementata correttamente, l'investimento risulta inutile. L'implementazione di un framework relazionale degli oggetti cattura tale investimento. E' possibile riutilizzare il suddetto framework in applicazioni successive, riducendo il costo di implementazione relazionale degli oggetti a meno del 10% dei costi di implementazione totali. Il costo più importante da considerare in caso di implementazione di un sistema è la manutenzione. Oltre il 60% dei costi totali di un sistema durante il relativo ciclo di vita può essere attributo alla manutenzione. Un sistema relazionale di oggetti implementato in modo non corretto è un incubo della manutenzione sia a livello tecnico che finanziario.

Caratteristiche essenziali di un framework relazionale degli oggetti

  • Prestazioni. E' necessario considerare alcuni elementi durante la scomposizione di oggetti in dati e la composizione di oggetti dai dati. In sistemi in cui la produttività dei dati è elevata e critica, ciò costituisce spesso un tallone di Achille di un livello di accesso progettato in modo inadeguato.
  • Riduzione al minimo dei compromessi di progettazione. Un modello familiare per tecnologi di oggetti che hanno creato sistemi che utilizzano database relazionali, è cambiare il modello di oggetto per facilitare la memorizzazione in sistemi relazionali e modificare il modello relazionale per una memorizzazione di oggetti più semplice. Sebbene siano spesso necessari modifiche insignificanti, un livello di accesso ben progettato riduce al minimo il degrado di progettazione sia del modello relazionale che dell'oggetto.
  • Estendibilità. Il livello di accesso è un framework scatola bianca, che consente agli sviluppatori dell'applicazione di estendere il framework nel caso, al suo interno, occorra aggiungere una funzionalità. Generalmente, un livello di accesso supporterà, senza estensione, il 65-85% dei requisiti di memorizzazione dati di un'applicazione. Se il livello di accesso non è progettato come framework estendibile, raggiungere l'ultimo 35-15% dei requisiti di memorizzazione dati di un'applicazione potrebbe essere molto difficile e dispendioso.
  • Documentazione. Il livello di accesso è sia un componente scatola nera che un framework scatola bianca. L'API del componente scatola nera deve essere definita in modo chiaro, ben documentata e compresa in modo semplice. Come indicato in precedenza, il livello di accesso viene progettato per essere esteso. Un framework estendibile deve essere documentato in modo estremamente preciso. Occorre identificare le classi che si desidera sottoclassare. Le caratteristiche di ogni protocollo della classe rilevante devono essere specificate (ad esempio pubblica, privata, protetta, finale, ...). Inoltre, una parte sostanziale di progettazione del framework del livello di accesso deve essere esposta e documentata per facilitare l'estendibilità.
  • Supporto per mappature comuni relazionali degli oggetti. Un livello di accesso dovrebbe fornire un supporto per alcune mappature relazionali di base degli oggetti, senza necessità di estensione. Tali mappature vengono discusse ulteriormente in una sezione successiva di questo documento.
  • Interfacce di persistenza: In un'applicazione OO (object-oriented), il modello di business per un'applicazione di oggetti cattura la conoscenza semantica del dominio del problema. Gli sviluppatori dovrebbero manipolare e interagire con gli oggetti senza doversi preoccupare troppo dei dettagli di richiamo e di memorizzazione dati. Una sottoserie ben definita di interfacce persistenti (di salvataggio, eliminazione, rilevazione) dovrebbe essere fornita agli sviluppatori dell'applicazione.

Servizi comuni relazionali degli oggetti

Stanno emergendo modelli comuni per applicazioni relazionali degli oggetti. I professionisti di IT che hanno ripetutamente oltrepassato il baratro, stanno iniziando a comprendere e riconoscere determinate strutture e funzionalità che evidenziano applicazioni relazionali degli oggetti. Tali strutture e funzionalità sono state formalizzate dalle specifiche dei servizi CORBA di livello elevato (che si applicano in modo altrettanto adeguato a sistemi basati su COM/DCOM).

Le specifiche del servizio CORBA applicabili e utili da considerare per la mappatura relazionale degli oggetti sono:

Le seguenti sezioni utilizzeranno tali categorie per strutturare una discussione di servizi comuni relazionali degli oggetti. Si incoraggia il lettore a fare riferimento alle specifiche CORBA appropriate per ulteriori dettagli.

Persistenza

La persistenza è un termine utilizzato per descrivere il modo in cui gli oggetti utilizzano un mezzo di memorizzazione secondario per mantenere il proprio stato attraverso sessioni distinte. La persistenza fornisce all'utente la capacità di salvare gli oggetti in una sessione e di accedervi in una sessione successiva. Una volta ottenuto l'accesso ad essi, il loro stato (ad esempio, gli attributi) rimarrà esattamente uguale a com'era nella sessione precedente. In sistemi con più utenti, ciò potrebbe non verificarsi, poiché altri utenti possono accedere e modificare gli stessi oggetti. La persistenza è correlata ad altri servizi discussi in questa sezione. La considerazione delle relazioni, della simultaneità e di altri elementi è intenzionale (e coerente con la scomposizione di CORBA dei servizi).

Esempi di servizi specifici forniti dalla persistenza sono:

  • Gestione connessione dell'origine dati: le applicazioni relazionali degli oggetti devono avviare la connessione all'origine dati fisica. I sistemi di database relazionali generalmente richiedono l'identificazione del server e del database. La specifica di gestione connessione tende ad essere specifica del fornitore database e il framework deve essere progettato e adattato di conseguenza in modo flessibile.
  • Richiamo degli oggetti: Quando gli oggetti vengono ripristinati dal database, i dati vengono richiamati dal database e tradotti in oggetti. Questo processo implica l'estrazione dei dati da strutture specifiche del database richiamate dall'origine dati, l'ordinamento dei dati dai tipi di database nei tipi e/o classi di oggetti appropriati, la creazione dell'oggetto appropriato e l'impostazione di attributi degli oggetti specifici.
  • Memorizzazione di oggetti: Il processo di memorizzazione di oggetti rispecchia il richiamo di oggetti. I valori degli attributi appropriati vengono estratti dall'oggetto, una struttura specifica del database (ad esempio, una stringa SQL, una procedura memorizzata o una chiamata di procedura remota) viene creata con tali valori e inoltrata al database.
  • Eliminazione oggetto: Per gli oggetti eliminati dal sistema, è necessario eliminare dal database relazionale anche i relativi dati associati. L'eliminazione degli oggetti richiede l'estrazione delle informazioni appropriate dall'oggetto, la creazione di una richiesta di eliminazione (ad esempio, una stringa SQL, una procedura memorizzata o una chiamata di procedura remota) e l'inoltro della richiesta al database. Tenere presente che, in alcuni linguaggi, (ad esempio, Smalltalk e Java), l'eliminazione esplicita non è supportata; al contrario, è supportata una strategia denominata raccolta dati obsoleti. I framework di persistenza che supportano tali linguaggi devono fornire un modo alternativo per la rimozione dei dati dal database una volta che le applicazioni non fanno più riferimento ai dati. Una modalità comune per il database è il mantenimento dei conteggi di riferimento del numero di volte in cui altri oggetti fanno riferimento a un oggetto. Quando il conteggio di riferimento per un oggetto cade su zero, nessun altro oggetto fa riferimento ad esso e può essere possibile eliminarlo. E' possibile eliminare oggetti con un conteggio di riferimento zero, poiché anche quando non si fa più riferimento a un oggetto, è comunque possibile interrogarli. Un criterio del database relativo a quando l'eliminazione dell'oggetto è consentita è comunque necessario.

Query

La memorizzazione persistente di un oggetto ha poco senso senza un meccanismo di ricerca e richiamo di oggetti specifici. Le funzioni della query consentono alle applicazioni di interrogare e richiamare gli oggetti in base a una serie di criteri. Le operazioni di base delle query fornite da un framework di mappatura relazionale degli oggetti sono Torva e Trova univoco. L'operazione Trova univoco richiamerà uno specifico oggetto e Trova restituirà una raccolta di oggetti basata su un criterio di ricerca.

Le funzioni della query di memorizzazione dati variano in modo significativo. Le semplici memorizzazioni dati basate su un file possono implementare operazioni query rigide, mentre i sistemi relazionali forniscono un linguaggio di manipolazione dati flessibile. I framework di mappatura relazionali degli oggetti estendono il modello query relazionale per renderlo incentrato sull'oggetto anziché sui dati. I meccanismi di pass-through vengono inoltre implementati per potenziare la flessibilità della query relazionale e le estensioni specifiche del fornitore (ad esempio, procedure memorizzate).

Tenere presente che esiste un conflitto potenziale tra meccanismi della query basata sul database e il paradigma dell'oggetto: i meccanismi della query del database vengono guidati dai valori di attributi (colonne) in una tabella. Negli oggetti corrispondenti, il principio di incapsulamento impedisce la visualizzazione dei valori degli attributi; essi sono incapsulati dalle operazioni della classe. La ragione dell'incapsulamento è che rende l'applicazione più semplice da modificare: è possibile modificare la struttura interna di una classe senza preoccuparsi delle classi dipendenti, finché le operazioni visibili pubblicamente della classe non cambiano. Un meccanismo di query basato sul database dipende dalla rappresentazione interna di una classe, interrompendo in modo efficace l'incapsulamento. La sfida per il framework è impedire alle query di modificare la fragilità delle applicazioni.

Transazioni

Il supporto transazionale consente allo sviluppatore dell'applicazione di definire un'unità di lavoro atomica. Nella terminologia database, ciò significa che il sistema deve potere applicare una serie di modifiche al database o deve assicurare che non venga applicata alcuna delle modifiche. Le operazioni all'interno di una transazione vengono tutte eseguite correttamente o la transazione non viene completata correttamente. I framework relazionali degli oggetti dovrebbero almeno fornire una funzione della transazione di commit/rollback simile a un database relazionale. La progettazione di framework relazionali degli oggetti in un ambiente a più utenti può presentare numerose sfide e, per questo, occorre prestarvi attenzione.

Oltre alle funzioni fornite dal framework di persistenza, l'applicazione deve riconoscere le modalità di gestione degli errori. Quando una transazione ha esito negativo o viene interrotta, è necessario che il sistema ripristini il proprio stato a uno stato precedente stabile, generalmente consultando le informazioni relative a tale stato dal database. Quindi, esiste un'interazione molto stretta tra il framework di persistenza e il framework di gestione degli errori.

Simultaneità

I sistemi OO (object-oriented) e che prevedono più utenti devono controllare un accesso simultaneo agli oggetti. Quando più utenti accedono contemporaneamente a un oggetto, il sistema deve fornire un meccanismo per assicurare che le modifiche all'oggetto nell'archivio persistente si verifichino in modo prevedibile e controllato. I framework relazionali degli oggetti possono implementare controlli di simultaneità pessimistici e/o ottimistici.

  • Il controllo di simultaneità pessimistico richiede che lo sviluppatore dell'applicazione specifichi il proprio intento quando l'oggetto viene richiamato dall'archivio dati (ad esempio, di sola lettura, blocco di scrittura,...). . Se gli oggetti sono bloccati, è possibile che altri utenti vengano bloccati durante l'accesso all'oggetto e durante l'attesa del rilascio del blocco. Sarebbe opportuno utilizzare la simultaneità pessimistica e implementarla con cautela, poiché potrebbero verificarsi situazioni con condizioni di stallo (deadlock).
  • Il controllo di simultaneità ottimistico presuppone che sia improbabile che si acceda contemporaneamente allo stesso oggetto. I conflitti di simultaneità vengono individuati quando le modifiche vengono salvate nel database. Generalmente, se l'oggetto è stato modificato da un altro utente dopo il relativo recupero, verrà restituito un errore all'applicazione, in cui si indicherà un errore dell'operazione di modifica. E' responsabilità dell'applicazione rilevare e gestire l'errore. Ciò indica al framework di memorizzare nella cache i valori simultanei degli oggetti e di confrontarli rispetto al database. La simultaneità ottimistica è meno dispendiosa se sono presenti dei conflitti, ma lo è maggiormente se il numero di conflitti è abbastanza importante (a causa della necessità di rieseguire il lavoro in tal caso).

Tutte le applicazioni che utilizzano dati condivisi devono utilizzare la stessa strategia di simultaneità; non è possibile unire controlli di simultaneità ottimistici e pessimistici negli stessi dati condivisi o potrebbe verificarsi un danneggiamento. La necessità di una strategia di simultaneità coerente viene gestita meglio tramite un framework di persistenza.

Relazioni

Gli oggetti sono legati agli altri oggetti tramite relazioni. Un oggetto Ordine contiene numerosi oggetti Voce della riga. Un oggetto Libro contiene numerosi oggetti Capitolo. Un oggetto Impiegato appartiene esattamente a un oggetto Azienda. In sistemi relazionali, le relazioni tra entità vengono generalmente implementate utilizzando i riferimenti chiave esterna / chiave primaria. In sistemi OO (object-oriented), le relazioni vengono generalmente implementate in modo esplicito tramite gli attributi. Se un oggetto Ordine contiene LineItems, Ordine conterrà un attributo denominato lineItems. L'attributo lineItems di Ordine conterrà numerosi oggetti LineItem.

Gli aspetti della relazione di un framework relazionale degli oggetti sono interdipendenti con i servizi di query, transazione e persistenza. Quando un oggetto viene memorizzato, richiamato, sottoposto a transazioni o interrogato, occorre considerare i relativi oggetti correlati:

  • Quando un oggetto viene richiamato, occorre richiamare anche gli oggetti associati? In modo semplicistico, la risposta è Sì, ma tale operazione effettuata quando gli oggetti associati non sono necessari è estremamente dispendiosa. Un buon framework consentirà un mix di strategie.
  • Quando un oggetto viene memorizzato, occorre memorizzare anche gli oggetti associati come se fossero stati modificati? Ancora una volta, la risposta dipende dal contesto.

Sebbene sia concettualmente vantaggioso considerare separatamente i servizi relazionali comuni degli oggetti, le implementazioni del framework relazionale degli oggetti dipendono l'una dall'altra. E' necessario implementare i servizi in modo coerente non solo attraverso singole organizzazioni, ma su tutte le applicazioni che condividono gli stessi dati. Un framework è l'unico modo meno dispendioso per ottenere tale scopo.