POLITECNICO DI MILANO Polo Regionale di Como Facoltà di Ingegneria dell’Informazione Corso di Studi in Ingegneria Informatica ANALISI COMPARATA TRA DATABASE AD OGGETTI E DATABASE OBJECT RELATIONAL Tesi di Laurea di: Relatore: RIVA MATTEO RONCHETTI LUCA Prof. Fraternali Piero Correlatore: Ing. Brambilla Marco Anno Accademico 2003/2004 2 Capitolo 1 Introduzione ..................................................................................4 1.1 Finalità del lavoro ....................................................................................................................4 1.2 Analisi del problema ................................................................................................................4 1.3 Organizzazione dell’elaborato ................................................................................................5 Capitolo 2 Tecnologie utilizzate ...........................................................6 2.1 Introduzione alle basi di dati .................................................................................................6 2.1.1 Basi di dati relazionali ................................................................................................................7 2.1.2 Basi di dati Object Relational .......................................................................................................8 2.1.3 Basi di dati ad oggetti .................................................................................................................8 2.2 Introduzione a SQL ...............................................................................................................10 2.2.1 Storia di sql e panoramica del linguaggio .....................................................................................10 2.2.2 Definizione delle strutture dati e inserimenti .................................................................................11 2.2.3 Selezione dei dati ....................................................................................................................12 2.2.4 Modifiche ai dati memorizzati ....................................................................................................14 2.2.5 Funzionalità di SQL-3 estese agli oggetti .....................................................................................15 2.3 Introduzione allo standard ODMG ......................................................................................22 2.3.1 Storia dello standard.................................................................................................................23 2.3.2 Architettura dello standard ........................................................................................................23 2.3.3 Modello dei dati ......................................................................................................................23 Capitolo 3 Confronto tra i vari DBMS .....................................28 3.1 Differenze tra i vari DBMS ...................................................................................................28 3.2 Panoramica dei prodotti in commercio................................................................................31 3.3 Criteri di valutazione per la scelta dei prodotti da analizzare...........................................32 Capitolo 4 Oracle 9i (ORDBMS) .....................................................33 4.1 Breve storia del prodotto.......................................................................................................33 4.2 Architettura ............................................................................................................................34 4.3 Tipi di dati ..............................................................................................................................35 4.4 Funzionalità SQL99 implementate.......................................................................................36 4.5 Creazione dello schema .........................................................................................................37 4.6 Tools dedicati..........................................................................................................................38 Capitolo 5 Versant (OODBMS) .........................................................42 5.1 Breve storia del prodotto.......................................................................................................42 5.2 Caratteristiche........................................................................................................................42 5.3 Installazione............................................................................................................................44 5.4 Tipi di dati ..............................................................................................................................46 5.5 Creazione dello schema .........................................................................................................50 3 5.6 Tools dedicati..........................................................................................................................51 Capitolo 6 Applicazione d’esempio ...............................................53 6.1 Descrizione..............................................................................................................................53 6.2 Specifiche ................................................................................................................................53 6.3 Linguaggio utilizzato..............................................................................................................55 6.4 Implementazione con ORDBMS ..........................................................................................55 6.5 Implementazione con OODBMS ..........................................................................................57 6.6 Manuale utente.......................................................................................................................58 Capitolo 7 Conclusioni ..................................................................................61 Bibliografia .......................................................................................................................64 4 Capitolo 1 Introduzione 1.1 Finalità del lavoro Il lavoro svolto nell’elaborato è consistito nell’analisi comparata di due tecnologie per la memorizzazione persistente di dati attraverso DBMS, una già in forte sviluppo (Object-Relational), mentre l’altra non ancora così ampiamente diffusa (Object-Oriented). Si sono perciò analizzati due dei tanti prodotti in commercio per evidenziare le caratteristiche peculiari di ognuno. Infine si è sviluppata una semplice applicazione con lo scopo di mostrare le differenze tra i due prodotti(ORDBMS, OODBMS) a livello di programmazione. 1.2 Analisi del problema La maggior parte della programmazione è orientata agli oggetti: questo ha reso l’utilizzo di un DBMS relazionale puro complicato da gestire in quanto risulta difficile mappare logicamente gli oggetti in tabelle. Le basi di dati relazionali, infatti, sono un insieme di tabelle dove le colonne definiscono la struttura della tabella stessa, mentre le righe contengono i dati. Questo ha comportato, in fase di sviluppo, a pensare ed a descrivere il mondo in termini di record, progettando il modello dei dati con questi concetti semplicistici. Un'altra caratteristica dei sistemi relazionali è il supporto di un linguaggio dichiarativo di interrogazione normalizzato (SQL). Questo linguaggio è stato concepito per il modello relazionale pertanto permette di inserire, aggiornare, eliminare righe(Data Manipulation Language) e aggiungere o cancellare colonne, tabelle o indici(Data Definition Language). SQL risulta, dal punto di vista dei programmatori, non essere bene integrato nei linguaggi di programmazione procedurali in quanto ogni produttore di questa tecnologia definisce delle estensioni proprietarie di SQL (non normalizzate) per rendere il sistema usabile; ad esempio query ricorsive, outer join, etc. Infine, riguardo agli RDBMS, essi hanno tipicamente un’architettura centralizzata; tutte le operazioni del database sono eseguite a lato server, con il rischio di creare dei “colli di bottiglia” e un calo evidente delle performance al crescere delle connessioni simultanee verso il db. Con l’utilizzo della tecnologia OR, invece, e le aggiunte alle funzionalità SQL estese agli oggetti, il mapping diventa maggiormente trasparente permettendo di sviluppare in modo più efficace. Per fare questo, i sistemi OR introducono due nuove caratteristiche: • Nuovi tipi di dato • Attributi complessi La prima caratteristica permette di trattare con tipi di dato prima non presenti come dati multimediali o di creare strutture di dati complesse definiti dall’utente(Abstract Data Type). Questi tipi di dato sono più o meno integrati con la struttura relazionale e il linguaggio di interrogazione; inoltre non essendo la tecnologia di tipo Object-Oriented, viene a mancare il supporto all’ereditarietà. La seconda caratteristica permette per esempio di creare una nuova tabella con un’altra tabella come attributo. Questo per simulare la relativa associazione nel modello a oggetti. Per quanto riguarda la tecnologia OO (OODBMS), l’approccio cambia radicalmente in quanto la suddivisione logica dei dati in tabelle è assente. Il modello a oggetti è più ricco rispetto a quello relazionale; in molte implementazioni, un oggetto è un’istanza di una classe. Una classe è usata sia come modello per la creazione dell’oggetto(nel db), sia come modello per istanziare tipi di quella classe(nel linguaggio di programmazione). Le classi definiscono il comportamento e lo stato dei loro oggetti. Lo stato è definito dai valori di un insieme di attributi, mentre il comportamento da un insieme di metodi. L’idea base di un OODBMS è di memorizzare direttamente oggetti complessi nel database senza limitazioni nei concetti Object-Oriented supportati.Un sistema OODBMS non è una 5 rappresentazione costruita attorno ad un RDBMS, ma provvede completamente alla gestione del database attraverso la memorizzazione fisica degli oggetti stessi. 1.3 Organizzazione dell’elaborato L’elaborato presenta 7 capitoli. Dopo una breve introduzione del lavoro svolto, il capitolo 2 descrive le tecnologie utilizzate focalizzandosi sui diversi tipi di basi di dati, sul linguaggio di interrogazione SQL con le relative estensioni e sullo standard ODMG. Dopo questa panoramica, il capitolo 3 confronta le principali categorie di DBMS attraverso dei parametri comuni. I capitoli 4 e 5 entrano nel dettaglio per quello che riguarda i prodotti analizzati, rispettivamente Oracle 9i e Versant. Il capitolo 6 riguarda l’applicazione sviluppata per evidenziare le differenze nell’approccio tra una tecnologie ed un’altra; oltre a definire i prerequisiti dell’applicazione d’esempio (demo) il capitolo in questione presenta anche un manuale utente semplificato. Infine con il capitolo 7 si evidenziano le conclusioni cui si è arrivati con la fine del lavoro. 6 Capitolo 2 Tecnologie utilizzate 2.1 Introduzione alle basi di dati Una base di dati (BdD) è una collezione di dati correlati. Per dati s’intendono fatti noti che possono essere memorizzati e che hanno un significato implicito. Le caratteristiche fondamentali di una base di dati sono: • Numerosità di dati. Cioè una quantità di dati che non possono stare sulla memoria principale. Una problematica è quindi il trasferimento dei dati dalla memoria secondaria a quella principale. Un fattore di complessità è il numero di trasferimenti dei dati dalla memoria secondaria alla principale. • Persistenza dei dati: non ho una situazione di questo tipo quando i dati creati dal programma esistono solo durante l’esecuzione del programma (vengono cancellati con la chiusura del programma). I dati invece devono esistere prima e dopo la durata dell’esecuzione del programma. • Globalità dei dati: i dati di interesse di un programma sono locali; i dati invece devono essere di interesse per una pluralità di programmi. Una base di dati può essere di qualsiasi dimensione e di diversa complessità. Un sistema di gestione di basi di dati (DBMS: DataBase Management System) è un insieme di applicazioni le quali permettono agli utenti di creare e mantenere una base di dati. Il DBMS è perciò un sistema software con scopi generali (general – purpose software system) che facilita il processo di definire, costruire e manipolare una base di dati per varie applicazioni. Definire una base di dati implica specificare i tipi di dati, le loro strutture e i vincoli per i dati che devono essere memorizzati. Costruire la base di dati significa immagazzinare i dati stessi entro un certo mezzo di memorizzazione che è controllato dal DBMS. Manipolare una base di dati include funzioni come la sua interrogazione per recuperare dati specifici, il suo aggiornamento per rispecchiare cambiamenti nel mini-mondo e la generazione di prospetti (reports) a partire dai dati. Ci sono in ogni modo più dibattiti che consensi nella definizione di che cosa sia un DBMS. In generale comunque si riscontrano 3 categorie base per la definizione e differenziazione di un DBMS: • Modello dei dati • Linguaggio di interrogazione • Modello computazionale Per modello dei dati s’intende una definizione o descrizione completa della sua struttura e dei vincoli della base di dati. Questa definizione è memorizzata nel catalogo di sistema, che contiene informazioni come la struttura di ciascun file, il tipo e il formato di memorizzazione di ciascun dato e vari vincoli sui dati. Le informazioni memorizzate nel catalogo sono dette metadati e descrivono la struttura della base di dati principale. Il catalogo è usato dal software del DBMS e anche dagli utenti della Base di dati che hanno bisogno di informazioni sulla struttura della stessa. L’analisi della realtà implica la scelta di un metodo di studio e di progettazione delle strutture dati e delle relazioni che esistono tra di loro. Questo è in pratica ciò che viene chiamato con il nome di schema. È importante distinguere tra la descrizione della base di dati e la base di dati stessa. La descrizione è detta schema della base di dati (data-base schema); esso è specificato durante la progettazione della base di dati e non ci si aspetta che cambi frequentemente. Una rappresentazione grafica di uno schema è detta diagramma di schema. Ciascun oggetto dello schema si chiama costrutto dello schema. 7 Un diagramma di schema rappresenta solo certi aspetti di uno schema, come i nomi dei vari tipi di record e dei dati, e alcuni tipi di vincoli; altri aspetti non vengono specificati. Molti tipi di vincoli non sono rappresentati nei diagrammi di schema; I dati nella base di dati in un particolare istante di tempo costituiscono lo stato della BdD o l’istantanea della BdD. Si parla anche di insieme di occorrenze o istante nella BdD. Lo schema logico che definisce la struttura e la tipologia dei dati ivi contenuti si chiama albero di definizione. Nel modello relazionale è più che mai importante imparare a ragionare in termini di entità e relazioni. Un’entità è in pratica un oggetto definito di per sé con alcune caratteristiche dette attributi o proprietà. Un generico campo che ospita più dati può essere in relazione con un altro. Nel modello relazionale la definizione delle relazioni è essenziale per legare i dati tra loro e gestirli al meglio con facilità. Esistono diversi tipi di relazione: uno a uno (indicata anche con 1 : 1); uno a molti (1 : n); molti a uno (n : 1) o molti a molti (n : m). Per la rappresentazione delle entità e delle relazioni è parecchio utilizzato il cosiddetto modello entity - relationship (ER). Il modello relazionale permette di gestire le informazioni attraverso tabelle permanenti (i dati che vengono effettivamente memorizzati sul database) e tabelle risultato (le interrogazioni o query per estrarre le informazioni cercate). Una riga di una tabella viene chiamata tupla, così come una colonna viene chiamata dominio. Il numero di righe viene chiamato cardinalità della relazione e può variare nel tempo. Il numero delle colonne non dovrebbe variare nel tempo (a meno che non si voglia intervenire sulla tabella a livello strutturale) e viene espresso con il termine grado. Il linguaggio di interrogazione è il modo con cui si estraggono i dati dalla base di dati. I linguaggi sono ormai standardizzati e dipendono dal tipo di database. Il modello computazionale consiste nel modo di gestire le associazioni dei dati all’interno della base di dati. 2.1.1 Basi di dati relazionali Modello dei dati - Un RDBMS (Relational DataBase Manegement System) consiste in una o più tabelle composte da righe e colonne. Le righe corrispondono a record (Tuple); le colonne corrispondono ad attributi (Campi nel record). Ogni colonna ha un tipo di dato. I tipi di dati che possono essere utilizzati sono pochi. Ad esempio character, string, time, date, number and currency. Ogni attributo (campo) del record può immagazzinare solo un singolo valore. I campi di lunghezza variabile non sono supportati. Le relazioni non sono esplicite, ma derivate da valori in specifici campi. Le relazioni molti-a-molti richiedono una tabella intermedia che contiene unicamente le relazioni. Linguaggio di interrogazione - Una vista è un sottoinsieme di un database che è il risultato di un’interrogazione (query). In un RDBMS una vista è una tabella. Gli RDBMS usano SQL come linguaggio per la definizione, la gestione e l’accesso ai dati. I dati localizzati in base al valore di un certo campo in un record. I tipi di query supportate variano da semplici interrogazioni di singole tabelle a complicate interrogazioni di più tabelle. Modello computazionale – Tutto il processo è basato sui valori dei campi nei record. I record non hanno identificatori unici che rimangono immutati durante la vita della tupla. Non ci sono modi di referenziamento da un record ad un altro. L’esame del risultato di una query è fatto sotto il controllo di un cursore che permette all’utente di vedere un record alla volta. Questo impone di pensare e descrivere il mondo in termini di array, progettando il modello dei dati con questi concetti. Un'altra caratteristica degli RDBMS è la loro architettura centralizzata. Tutte le operazioni del database sono effettuate a lato server, creando evidentemente un collo di bottiglia con calo delle performance nel momento in cui ci sono più connessioni aperte simultaneamente. 8 2.1.2 Basi di dati Object Relational Modello dei dati – Le basi di dati Object Relational (ORDBMS) utilizzano un modello dei dati che aggiunge al modello relazionale le funzionalità estese agli oggetti. Tutte le informazioni sono ancora persistenti in tabelle, ma i dati possono avere una struttura più complessa. Questi tipi di dati sono detti astratti (ADT). Un ADT (Abstract Data Type) è un dato costruito dalla combinazione di tipi di dati elementari. Il supporto degli ADT è attraente in quanto le operazioni e le funzioni associate ai dati possono essere utilizzate per indicizzare, immagazzinare e recuperare record basati sul contenuto dei nuovi dati. Linguaggio di interrogazione – Un ORDBMS supporta una estensione di SQL . Il punto dell’estensione è il supporto del modello ad oggetti. Le estensioni includono query che coinvolgono oggetti innestati, attributi settati, inclusioni di metodi e funzioni e query che hanno a che fare con dati astratti. Un ORDBMS è ancora relazionale perché i dati sono immagazzinati in tabelle e SQL è il linguaggio per la definizione, la manipolazione e l’interrogazione. Il risultato di una query sono sempre tabelle o tuple. Modello computazionale – Il linguaggio SQL, con le estensioni per accedere agli ADT, è ancora la prima interfaccia del database. Il supporto diretto dei linguaggi ad oggetti e i loro oggetti continua a mancare forzando i programmatori a continuare a tradurre tra oggetti e tabelle. 2.1.3 Basi di dati ad oggetti Modello dei dati – L’idea di base di un ODBMS è di memorizzare direttamente object model complessi in un database, senza limitazioni per concetti object-oriented supportati (Fig. 2-1). Un ODBMS non è una rappresentazione che lavora attorno un RDBMS, ma mette a disposizione un completo database management con uno spazio fisico di memorizzazione degli oggetti. Fig. 2-1. Object mapping 9 Quando gli oggetti sono memorizzati in memoria, implicitamente durante la navigazione trasparente, o esplicitamente durante le query, si trovano in un’area speciale della heap della Java Virtual Machine, sotto il controllo di un object manager (OM). L’OM trasformerà i puntatori in OID e viceversa, processerà trasparentemente tutti gli oggetti dal server ogni volta che è necessario, e marcherà gli oggetti modificati che saranno poi aggiornanti dal server alla successiva commit. Inoltre il modello dei dati permette l’uso di un identificatore (OID) per referenziare ogni istanza persistente di una classe (Fig. 2-2). A seconda dell’implementazione, questo identificatore può essere fisico o logico, e distribuito o no. Fig. 2-2. OID Il modello dei dati supporta l’incapsulamento di dati e metodi, l’ereditarietà multipla e gli ADT (Abstract Data Types). I database ad oggetti combinano gli elementi dell’orientamento agli oggetti e la programmazione di linguaggi orientati agli oggetti con le capacità del database. Il risultato è un alto livello di congruenza tra il modello dei dati per l’applicazione ed il modello dei dati del database, riscontrabile nel minor volume di codice scritto, nonché in una maggiore manutenibilità e riusabilità del codice. Il modello a oggetti dello standard ODMG3 specifica due tipi di oggetti: oggetti e letterali. Esso definisce a sua volta 2 tipi di caratteristiche degli oggetti: operazioni e proprietà. Le proprietà possono essere sia attributi che relazioni. I database a oggetti possono supportare efficientemente ogni tipo di struttura, inclusi alberi e oggetti composti. Uno dei punti di forza del modello ad oggetti è l’abilità nel definire referenziazioni, o relazioni tra oggetti. Usando linguaggi ad oggetti, queste referenziazioni sono implementate con i puntatori. Quando si memorizza un oggetto che ne referenzia altri, l’ODBMS cambia lo stato degli oggetti puntati da volatili a persistenti. Per fare questo, gli OID sono usati come referenziatori unici e persistenti. Una volta nel database un oggetto si riferisce ad un altro attraverso il suo OID. Una volta ricaricati nella memoria, gli OID sono mappati trasparentemente in puntatori. Navigare con gli OID non è solo più elegante, ma è anche un modo più efficiente per rintracciare gli oggetti. Linguaggio di interrogazione – Un linguaggio orientato agli oggetti (C++, Smalltalk, Java) è il linguaggio sia per l’applicazione che per il database. Esso provvede ad una relazione diretta tra l’oggetto dell’applicazione e l’oggetto persistente nella base di dati. La definizione dei dati, la manipolazione e l’interrogazione riflettono questa relazione. Lo standard ODMG-93 definisce anche un linguaggio dichiarativo (OQL) per interrogare una base di dati a oggetti. OQL non è completamente compatibile con SQL. Il risultato di una query OQL può essere un atomo, una struttura, un letterale, un oggetto od un set di oggetti. 10 Modello computazionale – In un ODBMS, nonostante siano possibili interrogazioni dichiarative, l’interfaccia primaria per la creazione e la modifica di oggetti è direttamente la sintassi del linguaggio nativo. Inoltre, ad ogni oggetto nel sistema viene assegnato un identificatore (OID) che è unico ed immutabile durante la vita dell’oggetto. Un oggetto può contenere un OID che referenzia logicamente o punta un altro oggetto. Queste referenziazioni dimostrano la loro preziosità quando vengono associate ad oggetti con entità del mondo reale. Esse formano anche la base di alcune caratteristiche quali le relazioni bi-direzionali e gli oggetti composti. Nella maggior parte degli ODBMS gli OID diventano fisici (la referenza logica viene convertita in un puntatore ad uno specifico indirizzo di memoria) una volta che il dato viene caricato in memoria per essere usato dall’applicazione. Questa conversione, che viene chiamata pointer-swizzling, permette l’accesso agli oggetti memorizzati alla velocità di accesso della memoria a differenza degli approcci tradizionali che richiedono più tempo. Nella maggior parte delle implementazioni un oggetto è un’istanza di una classe. Una classe definisce il comportamento e lo stato dei suoi oggetti. Lo stato è definito dalla stato dei valori dei suoi attributi. Il comportamento è definito da un insieme di metodi o funzioni in linguaggio tradizionale. 2.2 Introduzione a SQL SQL (Structured Query Language) è un linguaggio per la gestione delle basi di dati (database) fondato sulle teorie del modello relazionale. La potenza descrittiva di SQL dipende dal fatto che il suo vocabolario include un numero di istruzioni piuttosto limitato, ma al tempo stesso molto flessibile. Del resto è giusto che sia così: una sintassi complicata e con troppe istruzioni comporta sforzi per ricordarla, non agevolando affatto lo sviluppo e l’implementazione delle interrogazioni alle strutture dati. In contrasto con i linguaggi tradizionalmente più complicati, SQL esce agevolmente dalla mischia e si rileva il linguaggio ideale per i database relazionali. Come tutti i linguaggi che si rispettino, anche SQL ha seguito un iter di standardizzazione. L’istituto che si è occupato di questi aspetti è l’ANSI (American National Standard Institute, Istituto Nazionale Americano per la Standardizzazione) che sviluppa e pubblica standard industriali e rappresenta l’ISO (International Organization for Standard) negli Stati Uniti. 2.2.1 Storia di sql e panoramica del linguaggio Nel 1987 il comitato ISO adottò la prima versione standard di SQL, l’ANSI SQL-86. La versione successiva standardizzata e utilizzata come riferimento si è avuta nel 1989 (versione ANSI SQL89), mentre le implementazioni di SQL attualmente a disposizione sul mercato fanno riferimento alla più recente standardizzazione ANSI SQL-92 (che alcuni chiamano semplicemente SQL2) o addirittura quella che è uscita successivamente (SQL3). Questa versione ufficiale di SQL ha continuato a seguire l’iter di standardizzazione del comitato ANSI per un certo periodo, ma la versione successiva è già in studio (SQL4). Le istruzioni di SQL standard possono essere suddivise essenzialmente in due gruppi: istruzioni per la definizione delle strutture dati (DDL, Data Definition Language) e istruzioni per la manipolazione, o modifica, dei dati (DML, Data Manipulation Language). Altre istruzioni particolari, che dipendono dall’implementazione del database, sono le TCL (Transaction Control Language); queste permettono di gestire le transazioni. Una transazione è una sequenza di comandi SQL che il database considera come una singola entità. Può essere vista come un’unità logica al lavoro. 11 DDL CREATE si usa per creare una struttura con un certo nome secondo le specifiche date, DROP si utilizza per eliminare una struttura esistente; DML DELETE serve a eliminare le righe specificate da una tabella, INSERT permette di aggiungere nuove righe in una tabella, SELECT per selezionare dati dalle righe di una o più tabelle, UPDATE consente di aggiornare i dati nelle righe selezionate di una tabella; TCL COMMIT serve a confermare le ultime operazioni impartite dopo una transizione avvenuta; ROLLBACK permette di ripristinare le condizioni precedenti alle ultime operazioni impostate, SAVEPOINT inserisce un punto si savepoint per annullare solo parte delle modifiche apportate. SQL permette essenzialmente di gestire tipi di dato relativi a numeri e a caratteri. Per quanto riguarda il primo genere, si possono rappresentare numeri esatti (cioè tutti quelli che sono di tipo intero) o approssimati (le rappresentazioni di numeri con cifre decimali e dotati quindi di una certa precisione). I tipi relativi ai caratteri, invece, possono consistere in singoli caratteri o stringhe (sequenze di caratteri di varia dimensione). SQL permette di impostare vincoli sulle colonne delle tabelle in modo da consentire al DBMS di memorizzare solo informazioni coerenti con lo schema stabilito. In questo modo ci si svincola da numerosi controlli che normalmente il programmatore doveva includere all’interno delle procedure di gestione degli applicativi. 2.2.2 Definizione delle strutture dati e inserimenti La prima operazione da fare è la creazione della base di dati. Per creare un nuovo database si usa l’istruzione CREATE DATABASE. Una tabella è una struttura bidimensionale, nel senso che i dati possono essere individuati all’intersezione di righe e di colonne. I nomi delle colonne sono in pratica i nomi dei campi, mentre ogni riga è un record di informazione, ossia un insieme di campi o attributi, anche se non viene memorizzato fisicamente come i classici record di altri linguaggi. Quando si desidera creare una tabella, ovviamente la si deve definire dandole un nome e indicando anche il nome e il tipo dei dati delle relative colonne. Per far questo, si capisce che prima di tutto occorre pensare attentamente alla forma strutturale della tabella stessa. Scendendo nei particolari possiamo dire che per risolvere un tipico problema di gestione delle informazioni attraverso un database, si deve costruire in pratica un insieme di tabelle correlate tra loro secondo opportuni criteri. La progettazione di un database è piuttosto complessa e richiede un attento lavoro di analisi. L’analisi è uno studio della realtà che si desidera concretizzare attraverso archivi ragionati e correlati tra loro. In sintesi, la progettazione del database implica la progettazione delle tabelle che lo costituiranno e delle relazioni tra le stesse; in pratica, significa rappresentare la realtà e le relazioni presenti tra i dati scelti. Durante la fase di progettazione si devono scegliere opportunamente le colonne che serviranno per stabilire le relazioni tra i dati (le chiavi) e si deve tener conto del processo di normalizzazione. Una chiave primaria (primary key) è una colonna della tabella (o una combinazione di colonne, nel qual caso si chiama chiave primaria composta) che permette di identificare univocamente le righe della tabella. Due diverse righe non possono avere mai lo stesso valore nella primary key e a ogni riga deve corrispondere sempre un valore. La chiave primaria non può essere nulla. Una chiave candidata è una colonna (o una combinazione di colonne) che può essere scelta come chiave primaria. Dopo aver scelto tra le colonne, o tra le combinazioni di esse, la chiave primaria, le altre diventano di conseguenza chiavi alternative o unique keys. Quindi, anche se in alcune tabelle possono esserci più colonne candidate a fungere da chiave primaria, una sola viene considerata dal progettista come primary key. 12 Una chiave esterna (foreign key) è una colonna di una tabella (o una combinazione di colonne) che fa riferimento a una primary key o a una unique key della stessa tabella o di un’altra tabella (di solito, comunque, non può far riferimento a una tabella di un database remoto). Ovviamente si tratta di riferimenti logici e non di puntatori fisici. Il dover mantenere tale corrispondenza tra queste colonne rientra nella cosiddetta integrità referenziale, che è una caratteristica fondamentale dei database relazionali. Per tale motivo il vincolo di chiave esterna viene chiamato anche vincolo di integrità referenziale. Anche in questo caso, se la foreign key fa parte di una primary key non può essere nulla. I constraint sono vincoli su tabelle definiti per restringere i valori ammissibili per una colonna o un gruppo di colonne. Con opportuni vincoli di integrità referenziale, è il RDBMS che si fa carico di controllare la validità dei riferimenti. Esistono diversi tipi di constraint: NULL o NOT NULL UNIQUE PRIMARY KEY FOREIGN KEY / REFERENCES CHECK Nella CREATE TABLE eventuali constraint possono essere indicati subito dopo il nome della colonna. Al momento della creazione delle tabelle sarebbe importante definire le relazioni tra le chiavi. In questo modo il DBMS controlla che vengano inseriti dati collegati nel modo opportuno o che non vengano cancellati per errore. Se il campo per la chiave è uno solo, è sufficiente digitare dopo la sua descrizione la parola riservata PRIMARY KEY (per la chiave primaria). Nel caso, invece, di chiavi composte da più campi, nella CREATE TABLE, dopo la descrizione delle singole colonne, bisogna specificare il tipo di chiave e tra parentesi la lista dei campi che la costituiscono. Se si rispetta l’ordine di inserimento dei dati così come sono stati definiti in fase di creazione della tabella e si inseriscono tutti i campi relativi alle colonne, non è necessario digitare ogni volta il nome dei campi stessi. Se, invece, si desiderano inserire solo alcuni dati corrispondenti a certi campi, allora è indispensabile stabilire in quali campi si effettuerà l’inserimento e anche in quale ordine (che non deve essere necessariamente lo stesso della definizione iniziale della tabella al momento della CREATE TABLE). 2.2.3 Selezione dei dati La selezione dei dati si effettua attraverso l’istruzione SELECT. Questa istruzione seleziona alcuni (o tutti i) dati contenuti nelle tabelle e li visualizza. Non è un caso che SELECT sia la parola più ricorrente nell’uso di SQL. L’istruzione SELECT ha diverse parole correlate (clausole): FROM WHERE STARTING WITH ORDER BY GROUP BY HAVING Un blocco di interrogazione viene chiamato anche query-block. Come si può intuire facilmente, WHERE è in pratica la seconda parola più ricorrente del linguaggio. E’ importante ricordare che con SELECT diciamo cosa vogliamo ottenere, ma dobbiamo anche specificare da dove vogliamo che le informazioni vengano prelevate. E’ possibile avere lo stesso 13 nome per campi di tabelle diverse ed è anche possibile estrarre dati contemporaneamente da due o più tabelle. Per tali motivi si comprende perché sia indispensabile specificare da quale tabella (o tabelle) si vogliono prelevare le informazioni. Un campo potrebbe persino risultare vuoto, a meno che non si forzi il database a controllare che questa condizione non si verifichi. A tal fine è sufficiente specificare l’opzione NOT NULL in fase di creazione della tabella, indicando quindi che il campo in questione non può assumere valore nullo. Bisogna però stare attenti a non confondersi: NULL è diverso dal blank, da uno o più spazi, o da 0 (zero) per i dati numerici. Corrisponde in pratica alla situazione di non esistenza o di assenza di dati; ecco perché è diverso dallo 0 e dagli spazi: questi sono considerati dei valori! Se si effettua un controllo tra un dato qualsiasi e uno NULL, il risultato sarà Unknown, poiché non è possibile confrontare qualcosa con il nulla. Quindi si può evincere che nei confronti di tipo boolean esistono tre possibili risultati: TRUE, FALSE e UNKNOWN. Naturalmente nella query, volendo, si possono specificare anche solo alcuni campi scelti da visualizzare e non tutti come nell’esempio. Molti analisti, dato che la situazione NULL è scomoda da gestire preferiscono definire tutti i campi come NOT NULL. Tuttavia esiste un inconveniente implicito in questa scelta: consiste nel fatto che in fase di inserimento dati, o di modifica della tabella, è sempre necessario specificare il valore (ad esempio, spazio per i caratteri o 0 per un NUMBER). Nelle query si utilizzano spesso gli operatori relazionali: = Significa ovviamente che il campo deve essere uguale al valore comparato (si legge ‘uguale a’). Oppure ! = indicano che il campo deve essere diverso dal valore comparato (si legge ‘diverso da’). NOT indica che l’espressione valutata non deve essere vera AND indica che entrambe le espressioni a destra e a sinistra dell’operatore devono essere vere OR indica che deve essere vera una qualsiasi delle due espressioni o entrambe < Significa che il campo deve essere minore di quello comparato (si legge ‘minore di’). > Significa che il campo deve essere maggiore di quello comparato (si legge ‘maggiore di’). <= Significa che il campo deve essere minore o uguale a quello comparato (si legge ‘minore o uguale di’). >= Significa che il campo deve essere maggiore o uguale a quello comparato (si legge ‘maggiore o uguale di’). Un altro operatore molto importante è LIKE, che serve a estrarre quei valori che assomigliano a un certo schema senza essere identici a esso. Il simbolo di percentuale (%) si usa per indicare un carattere jolly (o wildcard, in inglese), ossia una sequenza di caratteri qualsiasi. Per visualizzare una stringa contenente un carattere solo qualsiasi in una data posizione, e non in sequenza, si utilizza il simbolo di sottolineatura ( _ ) chiamato anche carattere jolly singolo. Altri operatori sono quelli logici: NOT, AND, OR. Questi operatori vengono calcolati secondo la priorità indicata in tabella. Spesso può essere necessario avvalersi dell’uso di parentesi per esprimere meglio le condizioni da valutare. Naturalmente il contrario di Unknown (NOT Unknown) rimane Unknown. In questo caso il contrario di una cosa sconosciuta non diventa una cosa conosciuta. Join Il join rientra tra le operazioni principali tipiche dei database relazionali, insieme alla proiezione delle colonne e alla selezione dei record. Molte pubblicazioni tecniche danno definizioni diverse dei tipi di join. Anche la documentazione ufficiale delle case produttrici di DBMS spiega l’argomento in modo differente e usa termini diversi per indicare lo stesso tipo di join implementato da altri. Nel nostro conteso si ritiene utile far comprendere la logica che guida nell’utilizzo del join e il riferimento alle parole chiave introdotte con la sintassi di SQL versione ANSI-92 (o SQL-92). 14 Se è vero infatti che in SQL ci sono molti modi per operare le stesse query, è anche vero che per agevolare gli utenti e i programmatori, con le versioni più recenti del linguaggio il comitato ANSI ha cercato di semplificare la sintassi e di mettere a disposizione termini riservati più precisi, puntuali e descrittivi. Il prodotto cartesiano (cartesian product) fra due tabelle (o due relazioni in senso lato) consiste semplicemente in tutte le possibili coppie di ogni record della prima relazione con ogni record della seconda. Per questo motivo in inglese viene chiamato anche cross join. Nell’SQL-89 per operare il prodotto cartesiano era necessario specificare la lista delle tabelle implicate, come nell’esempio che ci apprestiamo a valutare. La versione ANSI SQL-92 precede, oltre a questo metodo compatibile con la versione precedente, anche le parole riservate CROSS JOIN che vanno digitate tra i nomi delle tabelle (al posto quindi della virgola). Ovviamente in entrambi i casi il risultato è l’insieme delle coppie delle righe delle due tabelle in questione, con tutti i campi poiché abbiamo scelto l’operatore *. Spesso il prodotto cartesiano è troppo ridondante rispetto alle necessità di interrogazione, per cui di solito si opera un’ulteriore discriminazione dei dati attraverso la clausola WHERE. Quando si opera una query inserendo una condizione si ha in pratica un predicato o condizione di join (join condition). Il join viene perciò chiamato join di condizione (condition join). 2.2.4 Modifiche ai dati memorizzati Inserimento di dati Per inserire i dati si usa l’istruzione INSERT. Un metodo veloce per popolare una tabella con i dati di un’altra tabella, sempre che questa sia perfettamente compatibile con la prima. Nel caso non ci fosse una perfetta corrispondenza nella struttura della tabella, si possono avere problemi. In ogni caso, è molto importante rispettare l’ordine di inserimento dei dati secondo quello relativo ai campi, che comunque può differire dall’ordine indicato nella CREATE TABLE. Modifica di dati Per aggiornare una tabella si utilizza l’istruzione UPDATE. Dopo aver indicato quale tabella vogliamo aggiornare dobbiamo indicare quale campo subirà l’aggiornamento, attraverso il comando SET, e il nuovo valore che dovrà assumere. E’ importante ricordare che l’operazione di aggiornamento avviene indipendentemente dal contenuto precedente del campo o dei campi che vogliamo aggiornare. Ovviamente è possibile aggiornare anche più campi, specificando dopo SET gli assegnamenti da impartire ai rispettivi nomi di campo e per più record (righe). Cancellazione di dati Quando si parla di cancellazione di dati, si può pensare sia a singoli record sia a intere tabelle. Le istruzioni che prenderemo in considerazione sono DELETE e DROP. Per cancellare righe (o tuple) da una tabella si utilizza l’istruzione DELETE. Il sistema dovrebbe rispondere dicendo quante righe sono state cancellate. Per eliminare tutti i dati da una tabella (tutte le righe) si possono utilizzare i comandi DELETE FROM o DROP TABLE, con alcune differenze significative. DELETE FROM è un comando che permette di cancellare tutte le righe di una tabella. In pratica svuota la tabella senza tuttavia modificarne la sua forma strutturale. 15 Può capitare di voler eliminare una struttura dai nostri archivi. L’eliminazione è consentita attraverso l’istruzione DROP seguita dal tipo di struttura e dal suo nome. Se l’operazione è andata bene, il sistema risponderà: Table dropped. Si ricordi che non esiste la possibilità di tornare indietro con un comando ROLLBACK dopo l’istruzione di DROP che elimina fisicamente la struttura oltre che il suo contenuto. Controllo delle transazioni Per la maggior parte delle operazioni compiute è possibile tornare indietro eliminando l’ultima operazione svolta prima di una conferma (un po’ come annullare l’ultima operazione tramite l’undo dei programmi di grafica o con il comando Modifica - Annulla degli elaboratori testi). Il comando che consente il ripristino si chiama ROLLBACK. Per confermare che le operazioni effettuate invece vanno bene si usa il comando COMMIT. Dopo tale comando non è possibile tornare indietro. Dopo aver impartito un comando come COMMIT o ROLLBACK, è bene verificare quale sia la reale situazione delle tabelle che stavamo gestendo, per maggior sicurezza. In effetti, una transazione comincia dopo COMMIT o dopo aver impartito il comando SET TRANSACTION. L’istruzione ROLLBACK ovviamente annulla le operazioni dell’ultima transazione eseguita e ripristina i dati come erano prima di iniziare la transazione stessa. Naturalmente i dati, per essere ripristinati, devono essere memorizzati dal sistema in qualche zona di memoria apposita. Ma è il sistema di gestione del database che se ne fa carico in modo trasparente all’utente. L’unica cosa che si può modificare in alcuni DBMS è lo spazio dedicato a questi segmenti di rollback (rollback segment). Può risultare utile, comunque, inserire punti fermi all’interno di una transazione per salvare il lavoro svolto fino a quel punto; questo è possibile attraverso i cosiddetti punti di salvataggio o savepoint. Impartendo una ROLLBACK, si annullano le operazioni effettuate dopo quel punto particolare. Gestione dei privilegi Ogni componente dello schema può essere protetto tramite l’assegnazione di privilegi (autorizzazioni) ai vari utenti; un privilegio è caratterizzato da: La risorsa L’utente che concede il privilegio L’utente che riceve il privilegio L’azione che viene consentita sulla risorsa SQL offre diversi tipi di privilegi, tra cui: insert: per inserire un nuovo oggetto nella risorsa update: per modificare il contenuto della risorsa delete: per rimuovere un oggetto dalla risorsa select: per accedere al contenuto della risorsa Per concedere o revocare privilegi si utilizza il costrutto GRANT o REVOKE. 2.2.5 Funzionalità di SQL-3 estese agli oggetti SQL-3 estende la precedente versione supportando il paradigma a oggetti. Le estensioni riguardano: • Dati definiti dall’utente(ADT, row types e distinct types) • Costruttori per row types e reference types • Costruttori per collection types(sets, lists, multisets) • Funzioni definite dall’utente e procedure • Supporto per large objects(BLOB e CLOB) In SQL-3 un dato di tipo astratto è definito specificando un insieme di dichiarazioni di attributi che rappresentano il valore dell’ADT, le operazioni che definiscono l’uguaglianza e l’ordinamento delle 16 relazioni e le operazioni che definiscono il comportamento dell’ADT stesso. Le operazioni sono implementate da procedure chiamate routines. Gli ADT possono a loro volta essere definiti come sottotipi di altri ADT. Un sottotipo eredita la struttura del suo supertipo consentendo l’ereditarietà multipla. Le istanze di ADT possono essere immagazzinate nel database solo attraverso la memorizzazione in colonne di tabelle. Esempio: CREATE TYPE CONTATTO_TYPE AS OBJECT (NOME VARCHAR2(20), COGNOME VARCHAR2(20), INDIRIZZO VARCHAR2(20), TELEFONO VARCHAR2(15), FAX VARCHAR2(15), EMAIL VARCHAR2(15), CELLULARE VARCHAR2(15), PROFESSIONE VARCHAR2(20), DT_NASCITA VARCHAR2(10) ) Una row type è una sequenza di campi coerente con la definizione della tabella. Due row types sono equivalenti se entrambe hanno lo stesso numero di campi e nella stessa posizione sono dello stesso tipo. Le row type sono un tipo di dato che rappresenta una riga in una tabella, così che intere rows possono essere memorizzate in variabili, passate come argomento a routines e ritornate come valore da funzioni invocate. Una named row type è un tipo row con un nome assegnato. Una named row type è effettivamente un tipo di dato definito dall’utente con una struttura interna non incapsulata consistente unicamente dei suoi campi. Una named row type può essere usata per specificare i tipi delle righe nella definizione delle tabelle. Una named row type può essere usata per definire un reference type. Il valore di una reference type definita per una specifica row type è unico ed identifica una sola istanza della row type in tabella. Il valore di una reference type può essere memorizzato in una tabella e usato per referenziare(puntare) direttamente una row specifica in un’altra tabella, come l’OID in un modello ad oggetti permette di referenziare un oggetto mediante un altro. Lo stesso valore di una reference type può essere memorizzato in rows multiple, permettendo di condividere la referenziazione tra le rows. Esempio: CREATE TYPE DATA AS (GIORNO VARCHAR2(2), MESE VARCHAR2(2), ANNO VARCHAR2(4), ORA VARCHAR2(5) ) Il valore di una reference type può riferirsi ad una riga in ogni tabella avente righe del tipo della reference type. Se la clausola SCOPE è specificata nella definizione di una tabella, le reference sono ristrette a righe in una singola tabella. L’uso di SCOPE non implica comunque nessuna integrità referenziale. 17 Esempio: CREATE TYPE APPUNTAMENTO_TYPE AS OBJECT (OGGETTO VARCHAR2(20), DESCR VARCHAR2(50), DURATA VARCHAR2(5), LUOGO VARCHAR2(15), DT_APP REF DATA_TYPE, UTE REF UTENTE_TYPE ) Collection types permettono di avere valori multipli nei campi delle tabelle. Esempio: CREATE TYPE LIST_CONT AS TABLE OF CONTATTO_TYPE I tipi BLOB(Binary Large Object) e CLOB(Character Large Object) vengono definiti per supportare oggetti di grandi dimensioni. Le istanze di questi tipi sono memorizzate direttamente nel database(invece che essere mantenute in files esterni). Esempio: CREATE TABLE DIPENDENTE (NOME VARCHAR2(20), MANSIONE CLOB(3M), FOTO BLOB(1G) ) Le tabelle possono essere anche estese con sottotabelle. Una tabella può essere dichiarata come sottotabella di una o più supertabelle usando la clausola UNDER associata alla definizione della tabella. Quando viene definita una sottotabella, la sottotabella eredità ogni colonna dalla sua supertabella e possono essere definite colonne aggiuntive. Esempio: CREATE TABLE AMMINISTRATORE OF AMM_TYPE UNDER UTENTE Operation Le operazioni che possono essere invocate in sql includono operazioni definite sulle tabelle(SELECT, INSERT, UPDATE, DELETE), funzioni implicitamente definite per gli ADT e routines o esplicitamente associate agli ADT o definite separatamente. Le routines associate agli ADT sono definizioni di funzioni per tipi specifici definiti dall’utente. Le definizioni di funzioni specificano le operazioni sugli ADT e ritornano un singolo valore di un tipo di dato definito. Le funzioni possono essere o funzioni SQL, completamente definite nello schema SQL, o funzioni esterne, definite in un linguaggio programmazione standard. Le funzioni SQL associate agli ADT sono invocate usando o una notazione funzionale o la dot notation. 18 Esempio: SELECT OGGETTO, LUOGO, DT_APP.ORA FROM TAB_APP WHERE DT_APP.GIORNO = ‘23’AND DT_APP.MESE = ‘5’ AND DT_APP.ANNO = ‘2004’ Le routines(procedure e funzioni) che definiscono aspetti del comportamento degli ADT possono essere incapsulate all’interno della definizione degli ADT(queste routines hanno accesso agli attributi privati degli ADT anche se possono essere definite al di fuori della definizione degli ADT. Un certo numero di routines ha un nome predefinito; per esempio, quando un ADT viene definito, una funzione costruttore è automaticamente definita per creare una nuova istanza. La funzione costruttore ha lo stesso nome del tipo creato e non prende parametri in ingresso e ritorna una nuova istanza del tipo creato i cui attributi sono settati al loro valore di default. La funzione costruttore è PUBLIC. Inoltre, per ogni attributo, sono automaticamente definite funzioni che settano o ritornano il valore dell’attributo stesso; queste funzioni possono essere esplicitamente definite in fase di programmazione. Possono essere definiti anche attributi virtuali: questi non hanno dei valori memorizzati; il loro comportamento deriva dalle funzioni definite dall’utente che settano o leggono i loro valori. Possono inoltre essere definite le funzioni di comparazione e ordinamento di ADT. La conversione tra diversi ADT è permessa mediante la definizione di funzioni di casting. Esempio: CREATE FUNCTION Importo_Euro (p IMPORTO) RETURNING DEC(10,2) BEGIN RETURN p/1936.27 END; Metodi Una routine SQL è sostanzialmente un sottoprogramma. Una routine può essere una funzione o una procedura. Una routine legge o aggiorna il valore di un ADT. Una routine è specificata dal nome, dai parametri in ingresso, dai valori in uscita(se è una funzione) e dal corpo costituito dal codice. Una routine SQL ha un corpo scritto completamente in SQL. Una routine esterna ha un corpo scritto in un linguaggio di programmazione. Stato SQL-3 supporta lo stato dei valori di vari tipi di dati. Per esempio lo stato di un ADT è la sequenza ordinata dei componenti dell’ADT memorizzati. Lo stato di una row è l’insieme ordinato dei valori delle sue colonne; e così via. Tempo di vita degli oggetti Un’istanza di un ADT può esistere in ogni punto dove il nome dell’ADT viene referenziato. Comunque, il solo modo che un ADT ha per essere memorizzato persistentemente in un database è di memorizzarlo nelle colonne di una tabella. Esempio: CREATE TABLE APPUNTAMENTO OF APPUNTAMENTO_TYPE 19 Non esiste un metodo per individuare una singola istanza di un ADT con le estensioni di SQL-3, e memorizzarla persistentemente nel database usando unicamente il nome. Similmente non c’è un posto nel quale tutte le istanze di un ADT esistono, a meno che l’utente non lo crei esplicitamente, definendo una tabella nella quale tutte le istanze vengano memorizzate. Quindi, in SQL-3 non è possibile applicare interrogazioni(query) a tutte le istanze di un dato ADT. Le istanze devono prima essere memorizzate in una o più tabelle(come valori di colonna). Una row in una tabella esiste fino a che non viene cancellata. La cancellazione di una istanza di ADT è ottenuta cancellando la riga nella quale è contenuta. Le routines in SQL-3 possono essere specificate all’interno della definizione dell’ADT o esternamente. SQL-3 supporta un modello a oggetti generalizzato . Eventi In SQL un trigger è un costrutto che viene implicitamente attivato ogni volta che l’evento associato ad esso si verifica. Quando un trigger viene attivato, l’azione specifica viene eseguita se è soddisfatta la condizione associata. I triggers possono essere utilizzati per vari scopi, come validazione di dati in ingresso, lettura da altre tabelle o per messaggi di allerta. Gli eventi che è possibile associare ad un trigger includono inserimenti, cancellazioni e aggiornamenti di tabelle e colonne. Una condizione associata al trigger può essere ogni condizione SQL ed una azione può essere ogni istruzione SQL. I trigger possono specificare quando il trigger dovrebbe essere attivato prima che l’operazione sia lanciata, o dopo. La condizione e l’azione possono essere eseguite per ogni row(FOR EACH ROW) implicata dall’istruzione SQL, o solo una volta per l’intero trigger(FOR EACH STATEMENT). Polimorfismo Routines differenti possono avere lo stesso nome. Questo prende il nome di overloading, e può essere richiesto, per esempio, per permettere ad un sottotipo ADT di ridefinire un’operazione ereditata da un supertipo. SQL-3 implementa quello che è conosciuto come un modello a oggetti generalizzato, il che significa che i tipi di tutti gli argomenti di una routine sono presi in considerazione al momento della determinazione di quale routine invocare, piuttosto che usare un singolo tipo specifico nell’invocazione come in C++ o Smalltalk. Come risultato, le regole per la determinazione di quale routine invocare possono essere complesse. L’istanza della routine che viene scelta per l’esecuzione è quindi la migliore scelta dati i tipi degli argomenti passati a run time. Incapsulamento Ogni componente(attributo o funzione) di un ADT ha un livello di incapsulamento che può essere PUBLIC, PRIVATE o PROTECTED. I componenti public formano l’interfaccia dell’ADT e sono visibili a tutti gli utenti autorizzati dell’ADT. I componenti private sono totalmente incapsulati, e sono visibili solo all’interno della definizione dell’ADT che li contiene. I componenti protected sono parzialmente incapsulati; sono visibili all’interno dell’ADT ed agli eventuali sottotipi dell’ADT stesso. SQL-3 supporta anche l’incapsulamento per le viste, che vengono considerate come sottotipo della tabella associata. Identità, uguaglianza e copia Di default, il test di uguaglianza tra attributi corrisponde al test di uguaglianza tra due istanze di ADT. Alternativamente, la specifica della dichiarazione di una funzione di un ADT è usata per determinare l’uguaglianza tra due istanze di ADT. 20 Due valori non sono uguali se entrambi hanno valore NULL. Due rows(o parti di esse) sono distinte se almeno uno dei rispettivi campi è diverso. Altrimenti sono identiche. Il risultato del confronto di due valori o due rows non è mai sconosciuto. Tipi e classi La forma più semplice di tipo definito dall’utente di SQL-3 è il distinct type, che permette all’utente di trattare tipi equivalenti di dati in modo separato. La parola chiave DISTINCT usata nella dichiarazione indica che il tipo risultante deve essere trattato in modo distinto da ogni altra dichiarazione dello stesso tipo. Ogni tentativo di trattare un’istanza di un tipo come un’istanza di un altro terminerà in un errore anche se ogni tipo ha la stessa rappresentazione. Ereditarietà e deleghe Un ADT può essere definito come sottotipo di uno o più ADT attraverso la clausola UNDER. In questo caso, l’ADT diventa un sottotipo dell’ADT specificato nella clausola UNDER. Un tipo può avere più di un sottotipo e più di un supertipo. Un sottotipo eredità tutti gli attributi e il comportamento dei suoi supertipi; attributi e comportamenti addizionali possono essere definiti. Un’istanza di un sottotipo è considerata un’istanza di tutti i suoi sottotipi. Un istanza di un sottotipo può essere usata ogni volta che un’istanza di un suo supertipo è prevista. Ogni istanza è associata con il tipo più specifico che corrisponde al più basso sottotipo associato all’istanza. In un dato istante, un’istanza deve avere esattamente un tipo specifico(in alcuni casi l’ereditarietà multipla deve essere usata per assicurare che questo sia vero). Il tipo più specifico di un’istanza non ha bisogno di essere necessariamente l’ultimo nodo dell’albero delle gerarchie. Un predicato TYPE permette all’istanza di un ADT di essere testata a runtime. Una definizione di un sottotipo ha accesso alla rappresentazione di tutti i suoi supertipi, mentre non ha accesso ai suoi sottotipi. Effettivamente, i componenti di tutte le rappresentazioni di supertipi sono copiate nella rappresentazione dei sottotipi con lo stesso nome e tipo di dato. Per evitare conflitti di nome, un sottotipo può rinominare componenti di una rappresentazione ereditata dal suo supertipo. Un supertipo può definire operazioni come ogni altro ADT. Un sottotipo può anche definire operazioni che hanno lo stesso nome delle operazioni definite per gli altri tipi, inclusi i suoi supertipi(overriding). Una famiglia di sottotabelle deve avere al massimo una supertabella. ad ogni riga di una sottotabella deve corrispondere esattamente una riga della supertabella. Viceversa ad una riga di una supertabella corrispondono più righe di una sottotabella. Le regole per le istruzioni SQL(INSERT, DELETE, UPDATE) sono definite in modo da tenere le righe in sottotabelle consistenti con ognuna delle altre; specificatamente: Se una riga è inserita in una sottotabella T, allora una riga corrispondente( con lo stesso identificatore e lo stesso valore di ogni colonna della sottotabella) è inserita in ogni supertabella di T seguendo la gerarchia delle tabelle. Se T non ha supertabelle, una riga è inserita solo in T. Se una riga è aggiornata in una supertabella, allora tutte le righe corrispondenti alle sottotabelle sono aggiornate. Se una riga è aggiornata in una sottotabella, allora ogni riga corrispondente è aggiornata ai nuovi valori. Se una riga in una tabella appartenente ad una famiglia di sottotabelle è cancellata, allora ogni riga corrispondente è cancellata. 21 Relazioni Le relazioni(tabelle) possono essere usate per definire relazioni n-arie come in SQL92; l’integrità referenziale così come altri vincoli possono essere definite in queste tabelle. Le colonne i cui tipi sono reference type permettono anche la modellizzazione delle relazioni in SQL-3. la referenziazione a gruppi di oggetti può essere specificata usando righe contenenti istanze di collection type (multiset, list, set) di SQL-3. Attributi Ci sono due tipi di attributi in un ADT: attributi memorizzati(stored) ed attributi virtuali(virtual). Un attributo memorizzato è specificato mediante un nome e un tipo di dato. Il tipo di dato di questo attributo può essere ogni tipo di dato conosciuto incluso un altro ADT. Ogni attributo memorizzato dichiara implicitamente un paio di funzioni: get(che ritorna il valore) e set(che setta il valore). Un attributo virtuale ha un valore che è derivato o computato da una funzione definita dall’utente. Le colone di tabelle possono anche essere usate per rappresentare attributi come in SQL92. Letterali In SQL-3 i letterali sono usati per specificare valori non nulli. Gli ADT non hanno valori letterali. Row type letterali sono formati concatenando valori da colonne singole. Contenimento SQL-3 supporta il concetto di valori contenuti in altri valori o altri ADT. Una forma di contenimento semantico può essere implementata specificando triggers che forzano la manipolazione in cascata di una collezione di struttura di dati quando uno di questi viene manipolato. Questo tipo di contenimento deve essere specificato dall’utente. Aggregati SQL-3 supporta row type come strutture letterali. Istanze di row type possono essere usate come valori in tabelle; le row type possono anche essere nestate. Un numero di collezioni parametrizzate predefinite sono anche definite. Una collezione deve essere specificata come un SET(<type>), MULTISET(<type>) o LIST(<type>). In ogni caso, il parametro <type> (chiamato element type) può essere un tipo predefinito, un ADT, una row type, o un’altra collection type. Un element type non può essere un reference type, ne può essere un named row type contenente un campo il cui tipo è reference type. Una collezione può essere usata come una semplice tabella in interrogazioni. In questo caso, ogni elemento della collezione corrisponde ad una riga nella tabella. La tabella viene trattata come se avesse una singola colonna il cui tipo è definito dal tipo dall’istanza della collezione. Siccome le collection type sono tipi di dato, esse devono essere dichiarate come dei tipi di colonne di tabelle per essere persistentemente memorizzate nel database. Estensibilità Nuove tabelle e tipi(ADT, row types, collection type, etc.) possono essere definiti su tipi esistenti. I tipi esistenti possono essere modificati aggiungendo nuove operazioni, attributi o vincoli. Le istanze esistenti non possono acquisire o perdere tipo senza la creazione di una nuova istanza e la distruzione di quella vecchia. Dinamicità Sono possibili limitate evoluzioni dello schema delle tabelle applicando la clausola ALTER. Le azioni che possono essere eseguite usando la clausola ALTER includono aggiunta, variazione, e eliminazione di colonne e aggiunta ed eliminazione di supertabelle e vincoli su esse. I tipi di dati possono anche essere aggiunti ed eliminati. 22 Metaclassi/ metaoggetti SQL-3 non ha nozioni di metaclassi e la sua semantica non è estensibile. Linguaggi ad oggetti Nuove istruzioni sono state aggiunte in SQL-3 per fare in modo da poter specificare completamente il comportamento degli oggetti attraverso il linguaggio SQL. Alcune istruzioni aggiunte includono: Una istruzione di assegnamento che permette al valore ritornato da una espressione SQL di essere assegnato ad una variabile locale, ad una colonna o ad un attributo di un ADT. Una istruzione CALL che permette di invocare una procedura SQL. Una istruzione RETURN che permette al valore di una espressione SQL di essere ritornato come valore di ritorno di una funzione SQL. Una istruzione CASE che permette la selezione di un percorso di esecuzione di blocchi di istruzioni SQL basato su scelte alternative. Una istruzione IF con THEN,ELSE, ELSEIF che permette la selezione di un percorso di esecuzione basato sul valore di una o più condizioni. Istruzioni per LOOP, WHILE e REPEAT che permettono la ripetizione di un blocco di istruzioni SQL. Controlli addizionali includono istruzioni composte e trattamento delle eccezioni. Un’istruzione composta è un’istruzione che permette di raggruppare in un blocco(block) una collezione di istruzioni SQL. Un’istruzione composta può dichiarare delle sue variabili locali e specificare delle eccezioni per gestire eventuali errori che si verificano durante l’esecuzione di ogni istruzione all’interno del gruppo. Per ogni eccezione, una dichiarazione condizionale stabilisce una corrispondenza uno a uno tra SQLSTATE e l’eccezione definita dall’utente. Lo standard SQL92 definisce l’interazione con un certo numero di linguaggi standard. Un aspetto chiave dell’interazione del linguaggio è la definizione della corrispondenza tra tipi di dati SQL e tipi di dati del linguaggio. In alcuni casi, questa corrispondenza è relativamente semplice; per esempio, il tipo di dato CHARACTER di SQL viene mappato sul tipo CHAR di C. In altri casi la mappatura non è immediata; per esempio, SQL92 supporta il tipo di dato TIMESTAMP, ma i linguaggi di programmazione standard non hanno un tipo di dato equivalente. In questi casi, SQL richiede l’uso di una funzione di cast per convertire il tipo TIMESTAMP al tipo CHAR a livello di programma e viceversa. Non ci sono invece tipi corrispondenti definiti per le strutture come ad esempio una riga di una tabella e un record o struttura o linguaggio di programmazione. 2.3 Introduzione allo standard ODMG ODMG(Object Database Management Group) e' un consorzio di industrie(O2, Object Design, Objectivity, Ontos, Versant, Poet, Servio, HP,Itasca, ....) istituito nel '91 per redigere uno standard con l’obiettivo di favorire la portabilità del software. ODMG 3 e' l'evoluzione di tre precedenti versioni. La prima versione, ODMG93, è dell'inizio '94. Il consorzio è affiliato al gruppo parallelo OMG, il quale si occupa di object orientation in generale. La caratteristica peculiare dello standard ODMG3 è l’integrazione delle funzionalità dei DBMS e dei linguaggi di programmazione object oriented in modo che gli oggetti della base dati appaiano a tutti gli effetti anche come oggetti del linguaggio di programmazione senza DMLintermediari. 23 2.3.1 Storia dello standard • • • • 1991 nasce lo standard ODMG in conseguenza all’apparizione sul mercato dei primi OODBMS 1993: ODMG93 1997: ODMG 2.0 1999: ODMG 3.0 2.3.2 Architettura dello standard Il modello a oggetti dello standard ODMG3 è direttamente derivato dal modello proposto da OMG con l’aggiunta di poche caratteristiche necessarie alla base di dati (Fig. 2-3). Similmente a SQL3, ODMG3 definisce un linguaggio per la specificazione di oggetti(ODL) indipendente dai vari linguaggi di programmazione ed un linguaggio per l’interrogazione funzionale della base di dati a oggetti(OQL) ispirato allo standard SQL. Fig. 2-3. Architettura 2.3.3 Modello dei dati Il modello dei dati di ODMG3 è fortemente tipizzato; tutti gli elementi di un dato tipo condividono il medesimo range di valori di stato (stesso insieme di proprietà) e un comportamento comune ( stesse operazioni). Tutti i tipi dichiarabili in ODMG sono caratterizzati da: • una specifica esterna (stato e comportamento astratti); • una implementazione (stato e comportamento concreti). La specifica esterna si articola in: • aspetti statici (proprietà o variabili di stato); • aspetti dinamici a loro volta suddivisi in: operazioni ammesse sulle proprietà; eccezioni sollevate dalle operazioni. 24 Nell’attuale standard l’implementazione degli aspetti sia statici sia dinamici è delegata al “binding” con uno specifico linguaggio (C++, Java, Smalltalk,...). I tipi semplici dichiarabili sono i letterali, i quali non hanno identificatori e sono categorizzati in: Atomici: long, short, unsigned long, unsigned short, float, double, boolean ,octet, char(carattere), string, enum(enumerazione); Collezioni parametriche: set<t>, bag<t>, list<t>, array<t>, dictionary<t,v>; Strutturati: date, time, timestamp, interval, struct .. {..}. Anche gli oggetti sono categorizzati in tipi; ciascun oggetto, a differenza dei letterali, possiede un unico identificatore; per questo motivo un letterale (senza identificatore) è detto immutabile mentre un oggetto (con identificatore) è detto mutabile. il comportamento è definito da un insieme di operazioni e lo stato dai valori delle rispettive proprietà suddivise in attributi e relazioni. Le proprietà ed il comportamento definiscono le caratteristiche di un oggetto. Gli oggetti di un dato tipo esibiscono lo stesso comportamento e condividono lo stesso spazio degli stati. I tipi degli attributi di un oggetto possono essere Literal od Object. Gli attributi di una “Interface” non sono una struttura (struct) in quanto l’interfaccia contiene solo operazioni. Gli eventuali attributi sono forme contratte delle due funzioni get e set. Tuttavia la classe che eredita l’attributo deve ridefinirlo come tale o come funzione. Un attributo non è un oggetto e non si possono definire attributi di attributi. Un attributo il cui tipo è un oggetto è una associazione monodirezionale. Le associazioni, come nell’ER, sono di tipo 1- 1, 1 - N, N - M, binarie e prive di attributi. Le associazioni possono essere definite solo tra oggetti. Esempio: Class Appuntamento relationship Utente ute L’integrità referenziale deve essere garantita dal sistema. La cardinalità N è realizzata da collezioni LITERAL. L’associazione non è un oggetto e non possiede proprietà. L’attraversamento di un’associazione nella direzione “molti” è governato dalle primitive della collezione che la definisce. L’implementazione delle associazioni può essere facilitata se macro espanse secondo il seguente schema raccomandato da ODMG: A) Cardinalità 1 relationship X y inverse Z ; attribute X y; //Variabile gestita da form_ e drop_ void form_y(in X un_x) raises(IntegrityError); void drop_y(in X un_x) raises(IntegrityError); Le procedure devono garantire l’integrità referenziale (side effect). 25 Esempio: class Impiegato …….. {……... relationship Manager manager inverse Manager::subalterni;…. } class Impiegato …….. {……... attribute Manager manager ; void form_ manager (in Manager m ) raises(IntegrityError); void drop_ manager (in Manager m ) raises(IntegrityError);…. } B) Cardinalità N relationship set<X> y inverse Z ; readonly attribute set<X> y; void form_y(in X un_x) raises(IntegrityError) ; void add_y(in X un_x) raises(IntegrityError) ; void drop_y(in X un_x) raises(IntegrityError); void remove_y(in X un_x) raises(IntegrityError); Esempio: class Manager …….. {……… relationship set<Impiegato> subalterni inverse Impiegato::manager... } class Manager …….. {……... readonly attribute set<Impiegato> subalterni; void form_ subalterni(in Impiegato imp ) raises(IntegrityError) ; void add_ subalterni(in Impiegato imp ) raises(IntegrityError) ; Per quanto riguarda il comportamento degli oggetti, le operazioni sono definite con la sintassi CORBA, uno standard prodotto dall’OMG per favorire l’interoperabilità del software: <tipo_risposta> <nome_op> (in | out | inout <Tipo_par1> <parametro1>, …) Ogni nome di operazione deve essere unico in un dato tipo; inoltre tipi diversi possono avere metodi omonimi (overloading di nome). Quando è richiamato un metodo su un oggetto viene selezionato quello specifico del tipo di quell’oggetto (semantica dell’operazione delegata all’implementazione, metodo). Una operazione può produrre side_effect. Il tipo di un oggetto può essere dichiarato con due meccanismi: interface o class; interface definisce l’oggetto tramite il suo comportamento astratto. E’ un approccio funzionale: tutte le proprietà (attributi, relazioni, operazioni) sono dichiarazioni di altrettante funzioni applicabili all’oggetto. Il tipo interface non può istanziare oggetti. Class definisce invece l’oggetto avvalendosi degli aspetti sia statici sia dinamici; inoltre è possibile istanziare oggetti di tipo class. Interface può essere ereditata da una classe per istanziare oggetti con il comportamento descritto dalle operazioni dell’interfaccia. Proprietà astratte (attributi e relazioni) di “Interface” devono essere ridefinite nella classe per implementarle sottoforma di attributi statici. (Una proprietà di “Interface” è una forma contratta delle due funzioni get e set,) L’Implementazione di un dato tipo di oggetto, specificato da “class”, consiste in: - una rappresentazione degli aspetti statici; - metodi abbinati ad operazioni definite nella classe o ereditate da una “Interface”; 26 in questo modo la separazione netta tra specifica esterna ed implementazione favorisce l’incapsulamento ed una molteplicità di implementazioni differenti Le classi e le interfacce possono essere in relazione gerarchica. Esempio: Interface Persona {…..} Interface Dipendente:Persona {…..} Interface Amministratore:Dipendente {…..} Si può dichiarare una classe in gerarchia con una interfaccia. Esempio: Class Utente:Persona {…..} In questo modo si possono istanziare oggetti della classe Utente con il comportamento dell’interfaccia Persona. L’estensione Extent (ammissibile solo nelle classi) di una classe è l’insieme di tutte le istanze del tipo abbinato alla classe all’interno di una particolare base di dati. Se un oggetto è istanza della classe A allora appartiene all’estensione di A. Se B è sottoclasse di A allora l’estensione di B è inclusa nell’estensione di A La creazione dell’estensione di una classe è a carico dell’utente. (Si noti che l’estensione di una tavola relazionale è automatica);le estensioni possono avere indici. Chiavi Come per SQL possono essere semplici (un solo attributo) oppure composte (più attributi racchiusi tra parentesi rotonde). Identificazione Univoca nell’ambito di una base di dati. Nomi Ad ogni oggetto istanziato è attribuibile uno o più nomi simbolici per accedervi via programma o in interattivo. Gli oggetti con nome sono di norma usati quali entry point della base di dati, ad esempio nomi di estensioni. I nomi sono simili alle variabili globali dei linguaggi di programmazione. Ciclo di vita degli oggetti Transienti, memoria rilasciata a fine programma. Persistenti, memoria gestita dal ODBMS. Il ciclo di vita è indipendente dal tipo. (E’ una differenza di fondo rispetto al modello relazionale). L’indipendenza dal tipo implica che le stesse operazioni agiscono indifferentemente su oggetti persistenti e transienti. (In relazionale i dati persistenti sono gestibili solo via SQL). Struttura delle dichiarazioni Interface e class in ODL (Object Definition Language) Alcune clausole sono opzionali: class <nome> [extends <superclasse> 27 [ : <interfaccia>:...]] (extent <nome della collezione> keys eventuali chiavi ) { attributi; relazioni; 1-1, 1-n, n-m operazioni; eccezioni } interface <nome> [ : <interfaccia>:….] { attributi; relazioni; 1-1, 1-n, n-m operazioni; eccezioni } L’ereditarietà in ODL Una classe eredita (extends) da al più una classe (ereditarietà semplice); Una classe può ereditare da più interfacce (:<interface>:... ereditarietà multipla, ma con il vincolo che le interfacce non contengano operazioni omonime); Le operazioni di una superclasse appaiono con la stessa segnatura (nome, tipo parametri e tipo risultato) nella sottoclasse; Il metodo (body) di una operazione ereditata può essere ridefinito. 28 Capitolo 3 Confronto tra i vari DBMS 3.1 Differenze tra i vari DBMS La scelta riguardo a quale tipo di DBMS utilizzare per diverse applicazioni si basa anche sulla valutazione di alcuni importanti aspetti tra cui: • • • • • • • • • • • Supporto per i linguaggi di programmazione Objcet Oriented Semplicità d’uso Semplicità di sviluppo Estensibilità Complessità delle relazioni Distribuzione e replicazione e federazione Maturità della tecnologia Esperienza dei programmatori e universalità di SQL Tecnologie proprietarie Applicabilità Trasparenza Supporto per i linguaggi di programmazione Object Oriented Gli ODBMS sono stati ottimizzati per un supporto diretto con le applicazioni Object Oriented. Tutti i concetti OO sono supportati(ad esempio incapsulamento, ereditarietà, polimorfismo). Il modello ODBMS è congruente con il modello ad oggetti dell’applicazione OO. Siccome le relazioni sono direttamente rappresentate, l’integrità referenziale è mantenuta dall’ODBMS. Il modello a oggetti usato dall’Object Database Management Group nello standard ODMG-93 è derivato dall’OMG Common Obect Model, con l’aggiunta dell’interazione con linguaggi tipo C++, Smalltalk e Java. Il modello a oggetti di SQL-3 è per sua definizione, un compromesso che aggiunge qualche supporto agli oggetti mentre mantiene la compatibilità con SQL-2. Gli ORDBMS hanno un supporto limitato per l’ereditarietà, senza una definizione consistente tra i produttori e il rispetto dello standard SQL-3. Per una applicazione OO che utilizza relazioni complesse per lavorare con un database relazionale o Object-relational, il codice deve essere scritto per mappare la complessa struttura a oggetti nel semplice modello del database relazionale. L’analogia sarebbe quella di smontare completamente la macchina prima di parcheggiarla in garage ogni notte per poi riassemblarla ogni mattina. Questo ha effetti sia sullo sviluppo sia sulle performance dell’applicazione. il codice scritto per gestire la mappatura può risultare consistente rispetto a tutto il codice dell’applicazione. Il mantenimento dell’applicazione degli ODBMS risulta anche più semplice rispetto agli altri tipi di DBMS. A runtime, le carenza di performance sono pagate muovendo i dati tra il database e l’applicazione. siccome questa mappatura dal db all’applicazione è lasciata all’applicazione, piuttosto che al DBMS, c’è il rischio della violazione d’integrità. Applicazioni differenti possono mappare in modo diverso creando inconsistenze. Semplicità d’uso I database relazionali, con le loro tabelle, righe e colonne, risultano una facile interfaccia per l’utente. Comunque, sono pochi gli utenti che usano il linguaggio SQL direttamente. I produttori di database relazionali hanno sviluppato dei tools che nascondono all’utente SQL e lo generano in background. Per quanto riguarda i database a oggetti, ci sono molti meno tools anche se si prevede 29 un aumento in futuro. Questo modello di tabelle con tipi di dati semplici è facile da usare, ma solamente se esso mappa in modo ottimale le strutture dati delle applicazioni. Questo approccio tradizionale ha portato ad una specializzazione dei programmatori di database che hanno acquisito esperienza nella mappatura da tabelle alle strutture delle applicazioni. I programmatori di database a oggetti, invece, trovano semplice usare direttamente gli oggetti senza forzarli in tabelle. Semplicità di sviluppo I database a oggetti trovano una piena applicazione nella programmazione object oriented. Infatti, questi database furono sviluppati per consentire una corrispondenza diretta tra l’applicazione OO e gli oggetti persistenti nel database. Il paradigma dello sviluppo orientato agli oggetti ha il vantaggio di essere una delle vie più naturali per modellizzare il mondo reale. Gli oggetti nel mondo reale vengono modellizzati direttamente uno ad uno nel software. Questa corrispondenza semplifica notevolmente lo sviluppo in tutto il ciclo di vita dell’applicazione e migliora il suo mantenimento. Essa semplifica anche l’integrazione di tools di produttori differenti attraverso le fasi del ciclo di vita del software. Il modello RDBMS implica una indipendenza dai dati del database rispetto a quelli dell’applicazione. Con SQL come linguaggio di interrogazione, ogni applicazione può accedere ed usare i dati in un modo indipendente. Questa procedura riduce i costi di mantenimento, in quanto ogni nuova applicazione non richiede la modifica del database anche se le nuove applicazioni hanno specifiche addizionali. Estensibilità L’uso tradizionale dei DBMS è stato limitato dal numero esiguo dei tipi di dati(string, integer, floating point, number, date, currency) supportati dal sistema. Per gli ORDBMS, estensibilità significa che agli utenti è permesso definire nuovi tipi di dati. In ogni caso, per gli ORDBMS esistono un numero di vincoli, i quali devono essere presi in considerazione dagli sviluppatori quando utilizzano gli ADT. Per esempio, le reference possono essere usate per referenziare una riga in una tabella, ma la row type deve essere di tipo named. Quindi, c’è un trade-off tra il vantaggio di una veloce referenziazione rispetto all’estensibilità degli ADT. Gli ODBMS sono estensibili perché le classi definite nell’applicazione possono essere definite da nuovi tipi persistenti. Nuove classi possono essere aggiunte dagli utenti con tutte le funzionalità del DBMS per le istanze persistenti di quelle classi. Complessità delle relazioni Le applicazioni, qualche volta, hanno delle relazioni di tipo molti a molti. Siccome gli ODBMS usano gli OID, essi sono più opportuni per le applicazioni che hanno da processare relazioni complesse. Questo perché risulta più semplice navigare tra reference successive, relazioni molti a molti bi-direzionali e propagazione di operazioni attraverso relazioni per formare oggetti composti. Gli ORDBMS supportano scarsamente queste capacità. La modellizzazione delle relazioni potrebbe richiedere tabelle addizionali in un RDBMS. Il processamento di solito include join multipli che richiedono risorse maggiori. Distribuzione, replicazione e federazione Un database distribuito memorizza i suoi dati trasparentemente attraverso volumi multipli ed anche differenti locazioni. Un database replicato ha tutte o porzioni dei suoi dati replicati in uno o più differenti luoghi e periodicamente sincronizza i contenuti dei suoi dati replicati. La replicazione dei dati è la base dell’immagazzinamento dei dati . Un database federato è un’unione logica tra database distinti ognuno in esecuzione su server indipendenti. 30 Nonostante gli RDBMS permettono ai server di comunicare con altri server e possono supportare differenti livelli di replicazione, gli ODBMS supportano una forma di distribuzione e replicazione aggiuntiva con una completa trasparenza attraverso server multipli e database federati, schemi multipli in una gerarchia, replicazioni con sincronizzazione automatica ed integrità. In aggiunta, gli ODBMS supportano funzionalità non presenti negli RDBMS, come l’evoluzione degli schemi dinamici con la migrazione automatica dell’istanza. Alcuni ODBMS supportano anche la memorizzazione ed il rintracciamento distribuito. Sia il partizionamento orizzontale (ogni classe è definita in ogni database) che quello verticale (alcune classi sono in un database, altre in un altro) possono essere supportati. La memorizzazione distribuita permette di usare specifiche politiche, come Round Robin, per memorizzare oggetti in differenti database fisici. Il rintracciamento distribuito significa che sono supportate delle query parallele. La distribuzione è una caratteristica fondamentale nel momento in cui è necessario il load balancing. Maturità della tecnologia Gli RDBMS sono stati sviluppati ed usati per più tempo rispetto ai database ad oggetti, quindi risultano essere una tecnologia più matura. I prodotti più maturi sono stati messi a punto per ottimizzare le performance e supportare un largo insieme di funzionalità, includendo il supporto per funzionalità avanzate quali processamento parallelo, replicazione, alta disponibilità, sicurezza e distribuzione. Esiste un’ampia varietà di tools e applicazioni che supportano gli RDBMS e lavorano con SQL. Apparentemente, gli ORDBMS dovrebbero trarre vantaggio da questo in quanto sono estensioni degli RDBMS. Il supporto per gli ADT richiede nuove capacità nel motore del DBMS, come il recupero, la concorrenza, l’indicizzazione e il caching; quindi questi tipi di dati non beneficiano automaticamente della maturità del RDBMS. Gli ODBMS stanno tuttora maturando con più vantaggi rispetto agli RDBMS. Inoltre sarà difficile per gli ORDBMS essere estensibili e conservare i loro vantaggi dovuti all’esperienza. Infine, mentre gli ORDBMS evolvono per supportare più funzionalità rispetto agli ODBMS, essi saranno basati su nuove estensioni della tecnologia diventando perciò la nuova, non sperimentata tecnologia confrontata con gli ODBMS che sono già maturi in queste aree con il motore del DBMS progettato nativamente per gli oggetti. Inoltre, nonostante gli ORDBMS provino ad estendere le architetture ereditate e l’implementazione del modello dei dati, i benefici degli ODBMS trovano fondamento dal supporto diretto degli oggetti. Esperienza dei programmatori e universalità di SQL Un vantaggio degli RDBMS e degli ORDBMS è la disponibilità di esperienza da parte degli sviluppatori e il grande numero di tools e supporti bibliografici. SQL è il linguaggio più universale dei database. Come risultato degli investimenti fatti dalle organizzazioni negli ultimi anni, la maggior parte degli sviluppatori ha familiarità con SQL e hanno tools con i quali sviluppano. Il meccanismo di accesso mediante ODBC minimizza il costo dell’adozione delle estensioni o delle nuove funzionalità dl database. Molti ODBMS, quindi, supportano almeno qualche funzionalità di SQL, ed alcuni le supportano tutte incluse le estensioni per supportare le query di oggetti usando metodi, relazioni, ereditarietà ed altro. Questo permette agli utenti degli RDBMS di iniziare ad usare gli ODBMS attraverso il familiare SQL e tools grafici, condividere gli stessi oggetti creati in Java o C++ e apprendere graduatamente a trarre vantaggio delle capacità aggiunte degli oggetti. Tecnologie proprietarie Le architetture di prodotto hanno un impatto diretto sulla strategia dei venditori. Un’architettura più aperta, possibilmente con API pubblicate e portabile attraverso varie piattaforme, crea opportunità 31 per altri potenziali partners di espandere il mercato. Di contro, un produttore di database desidera prevenire l’entrata di altri competitori che estendano i suoi prodotti. Nonostante i produttori incorrano in costi addizionali(e rischi tecnici) per supportare una propria tecnologia , i suoi partners possono lavorare sinergicamente per creare un mercato che è più grande e si espande più rapidamente. La questione è il controllo della definizione del prodotto e il controllo dell’evoluzione di tutte le possibili soluzioni previste nel contesto della tecnologia proprietaria. Prendendo come esempio Illustra, Informix è occupato a creare un software attorno al suo Universal Server. Dozzine di venditori hanno sviluppato tools per estendere le capacità disponibili. IBM invece ha utilizzato un approccio più cauto, scegliendo di essere l’unico provider di DB2. Applicabilità Per certe applicazioni gli ODBMS sono sicuramente superiori rispetto agli RDBMS o ORDBMS. Malgrado questo vantaggio molte organizzazioni ritengono più sicuro usare un database relazionale perché i produttori di RDBMS sono da più tempo sul mercato e offrono un’esperienza che i produttori di ODBMS non hanno. Infatti, a parte la Computer Associates, tutti i produttori di ODBMS non hanno raggiunto una grande dimensione. Comunque, essi hanno formato l’Object Datatabase Management Group e pubblicato uno standard(ODMG) per gli ODBMS per assicurare un grado di portabilità tra produttori che usano un ODBMS. Inoltre, il maggior rischio per gli utenti è continuare a provare nuove applicazioni su gli RDBMS. Trasparenza Per quanto riguarda gli ODBMS, la trasparenza è un modo di memorizzare e rintracciare gli oggetti in un database senza una query esplicita. Ogni volta che si usa la referenziazioni ad un oggetto, se quell’oggetto non è già in memoria, verrà automaticamente reso dal server. Ma, ogni volta che viene modificato lo stato dell’oggetto, verrà aggiornato nel database al successivo commit, senza necessità di parafrasare il codice java con il codice del database. In un ORDBMS, invece, la trasparenza viene a mancare in quanto sta allo sviluppatore aggiornare le associazioni attraverso i comandi SQL(INSERT, UPDATE). 3.2 Panoramica dei prodotti in commercio In Tab. 3-1 sono riassunti i principali produttori di DBMS con specificato il tipo (R, OR, OO) e la versione. Tab. 3-1. Prodotti in commercio Produttore Oracle Sybase Informix RDBMS Oracle 7.x System 10/11 Dynamic Server IBM DB/2 UniSQL Unisys Computer Associates Gemstone O2 OpenIngres ORDBMS Oracle 8.x, 9i, 10G ODBMS Universal Server(Illustra) Universal Database(DB/2 Extenders) UniSQL/X OSMOS Jasmine Gemstone O2 32 Object Design Objectivity Versant Postgree Object store Objectivity/DB Versant ODBMS Postgree 3.3 Criteri di valutazione per la scelta dei prodotti da analizzare I prodotti che sono stati scelti per essere analizzati sono ORACLE 9i e Versant ODBMS per quanto riguarda rispettivamente ORDBMS e ODBMS. Per quanto riguarda gli ORDBMS, la scelta è stata dettata dalla necessità di evidenziare il maggior numero di estensioni possibili, e il database della ORACLE si presta maggiormente a questo scopo in quanto tra tutti è quello che presenta il maggior numero di nuovi tipi di dati ed estensioni OR utilizzabili. Per quello che riguarda gli ODBMS invece, considerando anche la scarsa maturità della tecnologia si è cercato di unire le potenzialità del database VERSANT alla facilità di trovare supporti bibliografici. 33 Capitolo 4 Oracle 9i (ORDBMS) 4.1 Breve storia del prodotto Oracle nasce come Relational database fino a diventare Object-Relational con l’ultima versione che stiamo considerando. Ripercorrendo la storia del DBMS si possono evidenziare i seguenti step: 1988: Oracle Server V.6 Gestione automatica dei lock a livello di singola riga; Introduzione del linguaggio PL/SQL, estensione procedurale del linguaggio SQL; Maggiore scalabilità del sistema tramite il supporto di macchine multi-processore e macchine in cluster (Parallel Server); Introduzione di SQL*DBA; Tecniche sofisticate di backup e recovery “a caldo” che massimizzano la disponibilità dei dati in caso di crash del sistema; Tool Oracle*CASE per il controllo completo di tutte le attività connesse allo sviluppo di applicazioni (analisi, disegno dati, disegno dei moduli applicativi, generazione automatica di codice PL/SQL). 1992: Oracle Server V.7 Introduzione di una nuova struttura di memoria condivisa dalle applicazioni detta Shared Pool (SGA); Accesso trasparente a dati distribuiti sia in lettura che in modifica (Two-Phase Commit); Potenziamento della security con l’introduzione dei concetti di Ruolo e Privilegio di Sistema; Inserimento statement dichiarativi (Constraints) e scrittura di codice PL/SQL integrato alle tabelle (Triggers); PL/SQL utilizzato per creare stored procedures memorizzate nel database; Ottimizzazione delle query (tecnica Cost Based); manutenzione ed accesso in modo efficiente e flessibile a dati non strutturati (testi, video, immagini). 1997: Oracle8 Server V.8.0 Potenziamento delle funzionalità a supporto degli ambienti Data Warehouse e più in generale VLDB(Very Large Database); Introduzione dell’architettura NCA (Network Computing Architecture). 1999: Oracle8i Server V.8.1 Ulteriore potenziamento delle funzionalità rivolte ai VLDB; Integrazione di una Java Virtual Machine (JVM) nel kernel di sistema; Affiancamento del linguaggio Java al PL/SQL per lo sviluppo di applicazioni web-based. 34 2001: Oracle9i Server Unico Parser per il motore SQL e PL/SQL; Integrazione del modulo di Runtime per SQL e PL/SQL; Nuovi Date/Time Data Types; Migliore integrazione e maggiore flessibilità per i LOBs; Migliorato il supporto per XML; PL/SQL Object Inheritance; Nuovi funzionalità SQL; SGA dinamica – modifiche senza shutdown. 4.2 Architettura L’architettura di Oracle9i è mostrata in Fig. 4-1. Fig. 4-1. Architettura L’architettura interna di Oracle9i è thread-based. I threads sono oggetti all’interno di un processo che eseguono le istruzioni del programma. I threads permettono operazioni concorrenti cosicché un processo può eseguire diverse parti di programma simultaneamente su processori diversi. Un’architettura thread-based offre i seguenti vantaggi: • Faster context switching • Routine di allocazione della memoria globale più semplice • Gestione più veloce delle connessioni 35 • Decremento di carico computazionale Internamente, il codice che implementa il modello dei thread è compatto e separato dal corpo principale di codice Oracle. Le eccezioni e le routine allocano e deallocano risorse. Esse aggiungono robustezza senza perdite di prestazioni. Il database Oracle9i non è un tipico processo Windows; un’istanza Oracle (i threads e le strutture dati) è un servizio di Windows: un processo in background registrato all’interno del sistema operativo. Il servizio viene avviato dal sistema operativo e non richiede interfacce utente per partire. Quando si avviano istante multiple di Oracle su Windows, ognuna avvia il suo servizio con threads multipli. Shared Pool - Library Cache Contiene i metodi di esecuzione e la struttura degli alberi di parsing per gli statement SQL da svolgere sul database - Dictionary Cache Contiene dati dell’Oracle data dictionary Data Buffer Cache Contiene i dati recuperati dal database via SQL Statement. Si basa su due strutture: • dirty list • recently used (LRU) list Quando un processo utente necessita di un buffer di memoria, il processo server che gestisce il Data Buffer Cache scansiona la LRU list per trovare dei buffer liberi. La dimensione del Data Buffer Cache è definita dai parametri DB_BLOCK_SIZE* DB_BLOCK_BUFFERS nel file init<SID>.ora. 4.3 Tipi di dati Il Server diventa Object-relational; è così possibile il supporto alla definizione di dati complessi (record, array) e non strutturati (BLOBs - BinaryObjects) (Fig. 4-4). Fig. 4-4. Tipi di dati di Oracle9i 36 Oracle9i Server supporta la definizione di Object Views, speciali oggetti, implementati anche come tools, capaci di presentare dati presenti su tabelle relazionali “pure” come oggetti definiti tramite il comando CREATE TYPE. Questi tools(mapping tools) permettono ai programmatori di associare delle righe di una tabella relazionale ad istanze di una classe (es. oggetti). Inoltre permettono di vedere dinamicamente delle righe come oggetti puri, rimpiazzando le chiavi esterne con dei puntatori reali. Rappresentano perciò un utile strumento da usare con database relazionali già esistenti e nuove applicazioni objectoriented. Per quanto riguarda i dati predefiniti: CHAR NCHAR NVARCHAR2 VARCHAR2 LONG RAW LONG RAW NUMBER DATE CLOB: Character Large Object BLOB: Binary Large Object BFILE: External Files – Supportano fino a 4GB di informazioni per singolo dato – Accesso indicizzato ai dati – Accesso in lettura a dati memorizzati in file di Sistema Operativo – Interfaccia programmatica: C/C++ o PL/SQL VARRAY TABLE REF 4.4 Funzionalità SQL99 implementate CREATE TYPE Il costrutto CREATE TYPE permette la creazione di 3 tipi di dati: - Object types: astrazioni di entità reali le quali possono essere definite anche in maniera incompleta - Table types: set non ordinato di elementi di dati, tutti dello stesso tipo, costituito da una sola colonna ; il tipo Table può essere utilizzato come tipo di dati di una colonna di una tabella oppure come attributo di un object type o ancora come tipo di parametri o ritorno di funzioni - Array types: set ordinato di elementi di dati, tutti dello stesso tipo, costituito da una sola colonna. ROW TYPES E COLUMN TYPES Le row types sono tipi utilizzati come tipi oggetto, ovvero tipo di ogni istanza di tabella. Le column types sono tipi utilizzati come tipi di attributo; è così possibile dichiarare tabelle con attributi di tipo complesso o definito dall’utente. 37 METODI E COSTRUTTORI Per metodi si intendono funzioni o procedure; l’implementazione dei metodi avviene all’interno di un unico object body per tipo; i metodi possono accettare/ritornare parametri attraverso il comando IN, OUT oppure IN OUT; l’ordinamento può essere gestito tramite la clausola order by, mentre la clausola order davanti ad una funzione membro confronta due oggetti del tipo corrente ritornando un intero. Infine è possibile utilizzare l’opzione pragma al fine di dettagliare le capacità di un metodo. VISTE GERARCHICHE È possibile creare una vista ed interrogarla. ANY TYPE, ANY DATA SYS.ANYTYPE: descrizione di tipo(qualunque SQL type), named o unnamed. ANYTYPE è transiente: non è memorizzato nel database. SYS.ANYDATA: contiene un’istanza di un qualunque tipo, più la sua descrizione di tipo. E’ self-describing. Un ANYDATA può essere memorizzato nel database. SYS.ANYDATASET: è un insieme self-describing di dati di qualunque tipo: contiene la descrizione del tipo più un insieme di istanze di quel tipo. Si può creare o convertire(CAST) dati da qualunque tipo SQL ad ANYDATA e viceversa. 4.5 Creazione dello schema Per la creazione dello schema in Oracle 9i si fa uso di Oracle Enterprise Management console. Questo strumento permette di eseguire tutte le operazioni necessarie per creare lo schema del database tramite un wizard. Gli elementi che possono essere inseriti vanno da quelli relazionali (tabelle, viste, trigger, ecc.), a quelli ad oggetti (object type, tipi di array, tipi di tabella, ecc.) (Fig. 4-5). Fig. 4-5. Elementi inseribili nello schema 38 Nel wizard è presente anche la possibilità di visualizzare le istruzioni SQL associate all’operazione che si sta compiendo. Esempio: Creazione dell’Object Type “Utente”: CREATE TYPE UTENTE_TYPE AS OBJECT (NOME VARCHAR2(20), COGNOME VARCHAR2(20), INDIRIZZO VARCHAR2(20), TELEFONO VARCHAR2(15), FAX VARCHAR2(15), EMAIL VARCHAR2(15), USERNAME VARCHAR2(15), PASSWORD VARCHAR2(20), FL_AMM VARCHAR2(1), CONT ARRAY_CONTATTI ) Dall’esempio precedente si nota che ad Utente è associato un array di contatti. Esempio: Creazione del Tipo di Array “Array di Contatti”: CREATE TYPE ARRAY_CONTATTI AS VARRAY(1) OF CONTATTO Nell’esempio precedente è necessaria la creazione dell’Object Type “Contatto”. A questo punto è necessaria la creazione delle tabelle. Esempio: Creazione della tabella “Utente”: CREATE TABLE UTENTE OF UTENTE_TYPE 4.6 Tools dedicati A seguito dell’installazione di Oracle e a seconda della versione installata, saranno disponibili alcuni tools; questi applicativi possono essere lanciati separatamente ma per una integrazione dei servizi è stato sviluppata una console di gestione, la Oracle enterprise manager console. Oracle enterprise manager console Oracle Enterprise Manager è una soluzione integrata per amministrare e monitorare il DBMS. Enterprise Manager è basato su di un’architettura a pila composta da tre livelli (Fig. 4-5): • • • Console, applicazioni integrate e Management Packs Management Server(s) e database repository Intelligent Agents 39 Fig. 4-6. Struttura di Oracle Enterprise Manager Console centralizzata la responsabilità primaria della console è quella di presentare una user interface agli amministratori per tutti i loro compiti di gestione. In relazione a quanto installato sul sistema in uso, il primo livello dell’architettura potrebbe consistere dei seguenti componenti: Console: dalla versione 9.0 in poi le funzionalità di Oracle DBA Studio sono state completamente integrate nella console. Applicazioni integrate: Oracle Forms Server Manager, Oracle Policy Manager, Oracle Directory Manager, Oracle Net Manager, Oracle Spatial Index Advisor, Oracle Data Guard Manager, Oracle LogMiner Viewer, Oracle Enterprise Security Manager, Oracle Text Manager. Applicazioni dai Managements Packs: Oracle Diagnostic Pack: Oracle Performance Manager, Oracle Capacity Planner, Oracle TopSessions, Oracle Trace. Oracle Tuning Pack: Oracle Expert, Oracle Index Tuning Wizard, 40 Oracle SQL Analyze, Oracle Tablespace Map, Reorg Wizard, Outline Editor, Outline Management. Oracle Change Management Pack: Oracle Change Manager. Oracle Standard Management Pack: Oracle Performance Manager, Oracle Index Tuning Wizard, Oracle Create Baseline, Oracle Compare Database Objects, Oracle Advanced Database and Node Events. Oracle Management Pack for Oracle Applications: Oracle Performance Manager, Oracle Capacity Planner, Concurrent Processing Tuning Assistant, Oracle Applications Advanced Events. Oracle Management Pack for SAP R/3: Oracle Performance Manager, Oracle Capacity Planner, Oracle Advanced Events. Oracle enterprise manager console è disegnata per ridurre la complessità della gestione del DB server Oracle, come anche i componenti distribuiti, il software e l’hardware che supportano il DB. Siccome il primo livello dell’architettura dipende dal Management server(secondo livello), i clients sono in grado di funzionare senza overhead e carico computazionale dei servizi di management del sistema. Oracle Management server centrale, scalabile e affidabile Il secondo componente dell’architettura del tool in esame, il management server, è il framework engine. Il Management server mantiene una intelligenza centralizzata e un controllo distribuito tra i clients e i nodi del server. È responsabile per tutte le applicazioni back-end e i servizi critici(eventi di sitema, reporting, job system...) . Questo processo tra i clients e gli agenti intelligenti intercetta richieste dal primo livello, memorizza le informazioni in una db repository e distribuisce i compiti agli agenti intelligenti del terzo livello che li eseguono. Agenti intelligenti autonomi: il terzo ed ultimo livello consiste di managed targets e agenti intelligenti. I managed targets, che possono includere nodi, database, web server, application server ed altro, chiedono agli agenti di eseguire compiti(tasks) presi dal manager server. Una volta assegnati i tasks, agenti autonomi eseguiranno il lavoro programmato con noncuranza dello stato dei managed targets, dei management server o dei clients. Esempi di tasks significativi sono l’esecuzione di script SQL, il monitoraggio dello spazio disponibile in una tablespace, la creazione di backup settimanali, il monitoraggio in real-time del rate I/O del db o il monitoraggio della disponibilità dell’application server. 41 Enterprise manager console può essere avviata in modalità standalone o con connessione verso il management server. Quando si accede al tool in modalità standalone, la console non si connette al livello intermedio e cioè al management server. Siccome non richiede il management server o gli agenti intelligenti, la console in modalità standalone permette al singolo amministratore di creare sempici schemi di database, istanze e altri compiti connettendosi direttamente al database di interesse(database target). Questa modalità è stata utilizzata per lo sviluppo dell’applicazione d’esempio che ha richiesto uno schema di database molto semplice. 42 Capitolo 5 Versant (OODBMS) 5.1 Breve storia del prodotto Gli ODBMS apparvero sul mercato a partire dal 1980. L’obiettivo era quello di avere un database che fosse designato ed ottimizzato per memorizzare e manipolare oggetti. Inizialmente, invece di focalizzarsi sul modello dei dati (basato su un insieme determinato di tipi), il database si focalizzava su un modello ad oggetti definito con linguaggio O-O. Nel 1988, Versant ha iniziato a lavorare ad una architettura object-oriented che sia scalabile e distribuita e ha brevettato un algoritmo di caching. Il primo prodotto è stato il Versant Object Database Management System (ODBMS), che è stato visto come il primo database ad oggetti enterprise-scalabile. Autorevoli servizi di telecomunicazioni e finanza e compagnie di difesa e trasporto dipendevano da Versant per risolvere alcuni dei problemi delle applicazioni di data management. Applicazioni come la scoperta di frodi, analisi di rischio, management della produzione, collezione ed analisi di dati in real-time hanno beneficiato dall’unica architettura object-oriented di Versant. 5.2 Caratteristiche L’Object Data Management Group (ODBMG) ha definito dei linguaggi standard per C++, Smalltalk e Java per i database ad oggetti. Lo standard definisce: • Come un modello ad oggetti è definito ed immagazzinato in un database (Object Definition Language). Lo standard mette a disposizione un ODL separato, ma generalmente C++, Smalltalk o Java sono usati come linguaggi di definizione. • Come l’applicazione interagisce con l’Object Manager per manipolare gli oggetti (Object Manipulation Language). Questo definisce il linguaggio interfaccia per C++, Smalltalk e Java. • Come l’applicazione può fare una query sul database (Object Query Language). Ogni linguaggio interfaccia permette di esprimere ed eseguire query OQL. Solo i database ad oggetti con object-aware servers di passare e far eseguire la query al server. Gli altri usano il client per eseguire la query, trasferendo tutti gli oggetti dal database per poi verificare se c’è un’eguaglianza. Versant è stato progettato come un vero Database per oggetti, mettendo anche a disposizione operazioni su gruppi di oggetti. Consente così di minimizzare il carico sulla di rete e massimizzare la concorrenza grazie alla possibilità di gestire il Locking su singoli oggetti o gruppi. Inoltre, il supporto a transazioni estese, grazie alla possibilità di politiche di Optimistic Locking ed Event Management, permette prestazioni vicine a quelle di Database Memory-resident. L'architettura del Server Versant è interamente Multi-thread. Per applicazioni in grado di sfruttare Thread multipli, Versant mette a disposizione delle API Thread-safe. Questa possibilità mette in grado l'utente di sfruttare a pieno le potenzialità di sistemi multi-processore, consentendo di raggiungere prestazioni estremamente elevate. Inoltre, Versant offre la possibilità di accessi Multisession al Database, permettendo così ai Thread di creare transazioni multiple. Versant è tipicamente utilizzato in un’architettura a 2-tier (client/server) o a 3-tier. Questo spiega il supporto per thread multipli sia nel front-end di Versant (il client nel 2-tier, l’application server nel 3-tier), che nel back-and di Versant (il server). 43 Fig. 5-1. Architettura 2-tier di Versant Fig. 5-2. Architettura 3-tier di Versant A differenza degli altri prodotti, Versant utilizza l'identificazione logica degli oggetti. Il mantenimento dell'integrità referenziale del Database, la migrazione di oggetti attraverso la rete, il Clustering e la gestione dello spazio su disco avvengono automaticamente e in modo trasparente. Versant può referenziare indifferentemente oggetti in memoria, in un Database locale o remoto, attraverso le stesse modalità di navigazione tramite puntatori. La struttura Dual Cache dell'architettura Client/Server bilanciata di Versant, l'esecuzione delle Query e la gestione degli eventi sul Server permettono di sfruttare al meglio l'infrastruttura Hardware disponibile. Grazie all'integrazione con gli Application Server più affermati è possibile scegliere l'architettura applicativa più adatta, senza vincoli e con la garanzia di una scalabilità totale. 44 Versant offre standard elevati di continuità di servizio per applicazioni Mission Critical. Full Backup, gestione dello spazio su disco ed evoluzione dello schema vengono effettuati On-line, così come tutte le operazioni di Routine di manutenzione del Database. Versant offre un ricco panorama di soluzioni per il supporto allo sviluppo di applicazioni Objectoriented in C++, Smalltalk e Java. L'integrazione trasparente con questi linguaggi permette di memorizzare l'Object Model dell'applicazione, direttamente nel Database, senza le conversioni necessarie con un Database relazionale. La disponibilità di strategie di condivisione trasparente di oggetti tra linguaggi diversi, permette di utilizzare il linguaggio appropriato a ciascuna situazione di sviluppo. Inoltre sono disponibili integrazioni con i più diffusi strumenti di sviluppo sul mercato. Versant supporta lo sviluppo di applicazioni C++ su Database il cui schema viene derivato direttamente dai file Header del C++. Non necessitando di alcun preprocessore o compilatore speciale, Versant, grazie anche alla integrazione con i più comuni Tool di sviluppo, offre agli sviluppatori un alto livello di produttività con una bassa curva di apprendimento. Versant ha abbracciato Java come linguaggio universale per lo sviluppo di applicazioni. In Fig.5-3 è mostrata l’architettura di Versant. Fig. 5-3. Architettura di Versant 5.3 Installazione Configurazione Completata l’installazione sono state aggiunte delle informazioni nel registro: è stata aggiunta una riga alla fine del file etc/services, e sono state aggiornate delle variabili d’ambiente tra le quali CLASSPATH e PATH. Una tipica struttura ad albero di Versant è mostrata in Fig.5-4. 45 Fig. 5-4. Struttura installazione di Versant L’installazione di Versant Developer Suite permette di avere i componenti mostrati in Tab. 5-1. 46 Tab.5-1. Il Versant ODBMS mette a disposizione le funzionalità generali di un database con una elevata scalabilità per la gestione delle transazioni dual-cache per sistemi distribuiti multi utente. La Java Interface di Versant (JVI) permette una completa persistenza trasparente per oggetti di business senza cambiare modello delle classi java e permettere di memorizzare e condividere gli oggetti tra applicazioni diverse senza effettuare una mappatura. Come la Java Interface, l’interfaccia C++ di Versant permette una completa persistenza per oggetti C++, dove il modello dell’applicazione C++ è l’esatta rappresentazione del modello dati memorizzato in un database e condiviso tra applicazioni diverse. Un oggetto può essere recuperato, tramite la deferenziazione del suo puntatore. Il Toolkit XML di Versant mette a disposizione delle estensioni che permettono di rappresentare un database ad oggetti di Versant in XML. L’Asynchronous Replication di Versant (VAR) permette un aggiornamento differito da e verso surgenti di dati multiple. Questo facilita il load balancing in sistemi multipli ed eterogenei con un consistente aumento delle performance. 5.4 Tipi di dati Tipi Object Gli object sono utili se si vuole organizzare una grande quantità di codice e gestire dei tipi di dati complessi. Il termine object ha numerosi significati: Object: un “object” database è un componente software che può incapsulare dati e referenziare altri object. Istance Object: è un database object che può mantenere dati. In un database Versant, un object ha una classe che definisce quali tipi di dati sono associati all’object. 47 Class Object: quando si crea una classe, Versant crea uno speciale tipo di object chiamato “class object” o “schema object” che memorizza la definizione dei tuoi tipi di dati in un database. Si possono definire classi che contengono ogni genere di dato, includendo values, sounds, documents o referenziazioni ad altri object. Versioned Object: per tracciare l’evoluzione degli stati di una istance object, Versant permette di convertire una istance object in “versioned object”, questo perché ci possono essere numerose versioni di una istance object. Transient Object: esiste solo mentre il programma che lo ha creato è in fase di running. Persistent Object: è un object che può essere memorizzato in un database Versant. Stand-alone Object: viene creato con l’operatore new di C++, con l’operatore o la funzione new di C++/Versant o un object definito come variabile locale o globale. Embedded Object: è un object che è attributo di un altro object. Smart Object: seleziona automaticamente la versione corretta di un object basandosi su una configurazione pre-selezionata. Elementi di Object Nonostante gli object hanno una struttura singola, la loro implementazione coinvolge differenti elementi discreti: • Attributi: le parti di un object che memorizzano dati e associazioni con altri oggetti. Un attributo è persistente quando l’object che lo contiene è persistente. Gli attributi vengono definiti in una classe. Dopo che una classe è stata compilata, la definizione degli attributi è caricata in un database e memorizzata in object. I dati attuali sono memorizzati un una istanza di object. • Metodi: le parti di un object che contengono parti di codice eseguibile. I metodi sono definiti e implementati in una normale classe. Caratteristiche di Persistent Object Object Identity: Versant assegna ad ogni persistent object un identificatore unico chiamato logical object identifier (loid). Un loid è formato da due parti: il database identifier e l’object identifier. Il database identifier è l’identificatore per la creazione del database che è univoco in un sistema composto da più database. L’identificatore è creato da Versant quando il database viene creato. L’object identifier è l’identificatore creato da Versant quando l’oggetto è creato. L’object identifier è unico all’intero di un database. Object Migration: la capacità di migrare gli object rende i database distribuiti più pratici. Dopo che gli object sono stati creati, ci può essere la necessità di migrarli dove possono essere maggiormente usati, riducendo sia il traffico sulla rete che lo spazio occupato in memoria. Object e database identifier non cambiano dopo che un object è migrato o è cambiato. Anche quando un database o un object sono stati cancellati, gli identificatori non vengono riusati. Questo comporta non dover riscrivere il codice quando un oggetto migra. Ogni oggetto persistente deve avere una class object che contiene la definizione della classe. Quando un oggetto migra in un nuovo database, se non è stato definito nel database target, deve migrare anche una copia della sua class object. 48 Dynamic Binding: quando viene definita una classe che specializza una classe base, ci può essere la necessità di ridefinire dei metodi. I metodi ereditati possono essere ridefiniti in una sotto-classe. Polymorphism: è possibile fare l’overload di un metodo, definendo varie copie che differiscono dal numero e dal tipo degli argomenti. Situation Modeling: un vantaggio nell’uso del modello ad oggetti è la sua abilità a modellare le situazioni in una maniera realistica che può essere compresa da tutti. Schema Modification: dopo aver completato una applicazione ci può essere la necessità di estendere o modificare le sue classi per soddisfare nuovi task. L’insieme della definizione degli object associati ad un database è chiamato “schema” del database. In qualsiasi momento è possibile aggiungere o modificare lo schema di un database. Predefined Types Versant dispone di alcuni tipi elementari che possono essere usati con embedded, inheritance e association relationship. I seguenti tipi sono disponibili in tutte le interfacce: • Vstr: ha un overhead piccolo, ed una lunghezza di memorizzazione variabile per uno o più valori elementari. • Link: contiene la referenziazione ad altri object. • Link vstr: ha una lunghezza di memorizzazione variabile per uno o più referenziazioni ad altri object. C/Versant prevede anche il seguente tipo: • List: è un insieme ordinato di valori con lunghezza di memorizzazione variabile. C++/Versant prevede anche i seguenti tipi: • Bi-link: memorizza una referenziazione bidirezionale. • Bi-link vstr: ha una lunghezza di memorizzazione variabile per uno o più referenziazioni direzionali. • Array: è una collezione ordinata di elementi accessibile da un indice di tipo Integer. • Set: è una collezione non ordinata di elementi univoci. • Dictionary: è una collezione che mappa delle chiavi con dei valori. • List: ha una lunghezza di memorizzazione variabile con ordinamento • Date e Time: tipi attributo di tipo date e time. L’interfaccia C++/Versant permette di creare anche i seguenti tipi: • Paramaterized: garantiscono un’unica definizione per quelle classi che differiscono solo dal tipo di dato del loro contenuto. E’ utile per classi che hanno la stessa funzione ma hanno dei dati di tipo diverso. • Run-time: classi che sono definite a run-time. • Smart: selezionano automaticamente la corretta versione di un object basandosi su una configurazione pre-selezionata. Versant permette anche di usare third class libraries. Stato degli oggetti Quando si crea o si accede ad un database object, Versant mantiene informazioni sul suo stato corrente. Object transient, persistent status: quando si crea un object verifica se è transient o persistent. A livello di scope di una transazione i due tipi di object sono trattati nella stessa maniera, ma dopo un comando commit, solo gli oggetti persistenti vengono memorizzati nel database. 49 Object lock status: il lock permette un accesso garantito agli object. Per esempio un write lock garantisce che tu sei il solo user di un object, che è importante se vuoi aggiornare l’object. Object dirty status: quando un oggetto viene modificato, può essere marcato come “dirty”; questo significa che sarà aggiornato al successivo commit. Questo garantisce un miglioramento delle prestazioni in quanto dopo un commit, Versant non dovrà comparare il contenuto di ciascun object con il suo contenuto originale, per determinare quali object devono essere aggiornati. Object versioned status: è possibile tenere traccia dell’evoluzione degli stati di un object nel tempo, convertendo non-versioned object in versioned object. Quando un non-versioned object viene aggiornato, i suoi vecchi dati vengono sovra scritti. Quando un versioned object viene aggiornato, il vecchio object è preservato e il nuovo object è creato con i nuovi dati. Il nuovo object è chiamato “child”, mentre il vecchio object è chiamato “parent”. Versioned object che hanno lo stesso parent sono chiamati “siblings”. Ogni versione di un object ha un identificatore unico, un numero ed uno stato della versione, che può essere transient, working o released. Object configuration E’ possibile raggruppare specifiche versioni di differenti object in “configuration”, e automaticamente selezionare la versione corretta di un object, utilizzando un “smart object”. Object checkout status: è possibile fare check out su un object da un group database accessibile da vari utenti, verso un personal database accessibile da un solo utente. Quando si fa check out su un object, viene creata una copia nel personal database e viene messo un lock sull’object originale. Questo permette di lavorare sull’object per un periodo illimitato di tempo senza tener conto del traffico di rete. Object pin status: per garantire una gestione efficiente della memoria, Versant mette in una object cache gli object a cui si accede durante una transazione. Relazione tra object Object embedded relationship: una “embedded relationship” è creata quando un object viene usato come attributo di un altro object. Può anche essere chiamata “contains-a” o “containment relationship”. Usare embedded object garantisce un aumento delle performance, perché con un solo passo si accede ad una aggregazione di object, e il lock viene fatto su una sola entità. Object inheritance relationship: quando si definiscono degli object è possibile definire una struttura gerarchica. In questa gerarchia gli object discendenti ereditano attributi e metodi. L’ereditarietà è un modo efficiente di descrivere una situazione: una classe di base descrive le caratteristiche comuni alle classi derivate, mentre ciascuna classe derivata avrà delle caratteristiche proprie. Dato che le classi derivate, ereditano sia attributi che metodi, la definizione del codice diventa riusabile, estendibile e personalizzabile. Ci sono due tipi di ereditarietà: singola e multipla. L’ereditarietà singola descrive un object legato a più ancestor in una struttura a stack. L’ereditarietà multipla descrive un object legato a più ancestor in una struttura ad albero (tree). Object association relationship: una caratteristica importante del modello ad oggetti è l’abilità di associare object logicamente legati, ma di tipo differente. L’associazione tra oggetti è simile all’uso del puntatore tra due object, supponendo che ad entrambe sia stato associato un indirizzo virtuale di memoria. 50 Per creare dei puntatori persistenti al database, C++/Versant usa i “link”. I link vengono anche chiamati “smart pointer”, dato che rimangono validi anche se l’object viene spostato da un database ad un altro. L’uso dei link permette miglioramento delle performance: infatti è più rapido trovare un object in un database usando un link rispetto una query. 5.5 Creazione dello schema L’Enhancer è un tool che post processa classi java modificandole dopo essere state compilate. L’Enhancer accetta in ingresso file di tipo .class e produce una nuova classe in uscita. Il tool produce cambiamenti alle classi secondo le specifiche scritte in un file di configurazione. I cambiamenti delle classi includono: • Codice generato per definire lo schema del database ad oggetti; • Codice generato per leggere e scrivere gli oggetti persistenti su e da il database; • Codice modificato per aggiungere proprietà aggiuntive nell’accesso ai campi di un oggetto persistente. Esecuzione dell’Enhancer L’Enhancer può essere invocato in due differenti modi: • Come standalone utility • Come Java class loader Nell’applicazione d’esempio si è utilizzato il primo modo, per cui si farà riferimento solamente a quello. Standalone Enhancer utility Questa applicazione, quando è eseguita, esegue i seguenti passi: • Legge il file di configurazione sulla riga di comando; • Legge ogni file .class (o collezione di file in una directory) specificato sulla riga di comando; • Aggiunge codice ad ogni .class usando le informazioni contenute nel file di configurazione. L’invocazione del tool avviene tramite riga di comando: java com.versant.Enhance –in <cartellaSorg> -out <cartellaDest> -config <configFile> Il file di configurazione presenta una struttura del tipo: <categoria di persistenza> <classe java> Alcune categorie di persistenza sono: • “c”, Persistent Capable: gli oggetti della classe diventano persistenti esplicitamente (tramite invocazione di appositi metodi); • “p”, Persistent Always: gli oggetti della classe diventano persistenti quando l’oggetto viene creato; • “n”, Not Persistent Aware: l’Enhancer non modifica queste classi. Esempio creazione schema nell’applicazione d’esempio: Il file di configurazione per l’applicazione d’esempio ha la seguente struttura: 51 c progetto_tesi.Appuntamento c progetto_tesi.Contatti c progetto_tesi.Data c progetto_tesi.Utente Il risultato prodotto è la classe modificata (o collezioni di classi) più un ‘altra classe con nome esteso rispetto alla principale consistente in nomeClasse_Pickler_Vj.class. Per esempio per quanto riguarda la classe appuntamento il risultato prodotto sarà: Appuntamento.class Appuntamento_Picker_Vj.class La seguente Fig.5-5 mostra il risultato della chiamata del tool. Fig. 5-5. Esecuzione dell’Enhancer 5.6 Tools dedicati Uno degli elementi da valutare nella scelta di un database è la possibilità di avere a disposizione dal produttore un insieme di tools. Di particolare importanza sono le utility di backup e recovery, quelli che riguardano l’amministrazione del database e l’analisi delle performance. Nell’area del backup e del recovery, un produttore di database dovrebbe provvedere a delle capacità automatiche di backup. Dato che i principali ODBMS vengono usati per applicazioni distribuite è importante l’esecuzione di backup distribuiti attraverso database multipli. Un'altra funzione importante è l’uso di un roll-forward recovery log, il quale tiene traccia di tutte le transazioni fatte dall’ultimo backup. Tools dedicati all’amministrazione del database non devono essere in grado di supportare una interfaccia grafica (GUI), ma anche la possibilità di funzionare tramite linea di comando, con la possibilità di essere incorporati all’interno di scrip. Per un progetto in cui vengano utilizzate tecniche object-oriented, un ODBMS (Object Data Base Management System) è il miglior posto per mantenere oggetti persistenti. Le attuali tecnologie e gli strumenti disponibili sul mercato permettono di rimanere in un ambito strettamente object-oriented in ogni fase di progetto e, allo stesso tempo, i metodi di analisi, progettazione e sviluppo hanno completato una fase di maturazione soddisfacente con "best practice" ben definite. Le tecniche di produzione del software hanno dovuto subire una evoluzione, essendosi ridotti vertiginosamente i tempi di rilascio accettati dal mercato dell’e-business, e questo ha favorito la definizione di metodi "leggeri" che consentono cicli di sviluppo abbreviati. Il motivo più frequente è una progettazione che non tiene adeguatamente in conto le criticità dei sistemi distribuiti, in particolare la gestione delle transazioni e della persistenza degli oggetti. Il punto fondamentale e critico dello sviluppo è mantenere il rispetto del paradigma object-oriented in tutte le fasi di realizzazione del sistema. 52 L’impiego di database relazionali per dare persistenza agli oggetti è la più comune fonte di problemi nelle applicazioni a oggetti e a componenti. Ma è anche la più trascurata, malgrado sul mercato esistano soluzioni mature ed estremamente efficienti e scalabili. Laddove le tecnologie a oggetti vengono utilizzate con aspettative di successo, ormai più che giustificate, l’impiego di un ODBMS per la persistenza degli oggetti è la scelta naturale. I vantaggi che derivano dall’impiego di un ODBMS per la persistenza di oggetti sono molto evidenti in termini di minori costi e tempi di sviluppo e di migliori prestazioni e scalabilità. L’utilizzo di un database a oggetti permette di superare i limiti di scalabilità delle tecnologie tradizionali, di quelle relazionali in particolare. In un pieno rispetto del paradigma object-oriented, il modello delle classi e dei componenti è lo schema del database e non avviene nessuna conversione e semplificazione del modello nella gestione della persistenza. La complessità del modello objectoriented viene rispettata e la navigazione tra oggetti offre prestazioni più elevate di alcuni ordini di grandezza rispetto all’equivalente relazionale, oltre a forti risparmi nei costi di sviluppo, grazie all’assenza di qualsiasi strato di mapping. Un’entità reale non è "una singola riga di una singola tabella di un singolo database relazionale". La tecnologia degli ODBMS si può definire ormai decisamente matura. Nel corso degli ultimi anni l’affermazione delle tecnologie a oggetti è diventata una realtà, grazie anche a una evoluzione generale di tutte le tecnologie informatiche, che sicuramente hanno dato un contributo. Le tecnologie a oggetti in generale, e dei database in particolare, hanno avuto difficoltà di affermazione nel passato, anche a causa di alcune immaturità di altre tecnologie di contorno, come i protocolli di comunicazione e senz’altro l’hardware. Si tratta tuttavia di un passato ormai lontano. Il futuro si delinea chiaramente con una adozione delle tecnologie a oggetti che diventa sempre più massiccia. Del resto le esigenze dei mercati più competitivi, come quello delle telecomunicazioni o quello finanziario, non lasciano spazio a incertezze. Le necessità di prestazioni, scalabilità, flessibilità, tempi di realizzazione brevi e costi di possesso contenuti, accanto a una crescente complessità dei business da servire, non possono più essere soddisfatte con le tecnologie tradizionali. Ma, mentre nel settore degli strumenti di sviluppo e degli application server l’evoluzione a oggetti è ormai avvenuta e si sta completando, nel settore dei database c’è una maggiore inerzia, che è dovuta al notevole potere di mercato dei vendor dei DBMS relazionali. In questo scenario le prospettive per una sempre più ampia adozione degli ODBMS sono decisamente favorevoli. Una spinta decisa da parte delle necessità di mercato può accelerare il processo meglio di qualsiasi altra "naturale" evoluzione tecnologica. Fortunatamente, la risposta tecnologica è già pronta e disponibile. A seguito dell’installazione di Versant, sono disponibili alcuni tools: • Administration Console: per mezzo di una visualizzazione tramite tabella, vengono mostrati tutti i database presenti sulla macchina. Per ogni database è possibile vedere la lista delle classi, le transazioni attive, gli oggetti su cui si è effettuato un lock, la lista degli utenti e le connessioni effettuate; • Monitoring Console: permette di monitorare un database Versant attraverso un’interfaccia grafica • Object Inspector: mostra le classi e le loro istanze del database che è stato selezionato. La struttura ad albero permette di visualizzare lo schema delle classi, con eventuali superclassi o sottoclassi e la lista dei suoi attributi. 53 Capitolo 6 Applicazione d’esempio 6.1 Descrizione L’applicazione sviluppata consiste in un’agenda multi agente per la gestione degli appuntamenti e dei contatti. 6.2 Specifiche Gli attori coinvolti nell’utilizzo dell’applicazione sono 2: • Amministratore • Agente Use Case Fig. 6-1. Use Case Amministratore L’amministratore, nell’ambito della gestione degli appuntamenti (propri e no) può: • Visualizzare appuntamenti; • Inserire appuntamenti; • Modificare appuntamenti; • Eliminare appuntamenti. L’amministratore, nell’ambito della gestione dei contatti (propri e no) può: • Visualizzare contatti; • Inserire contatti; • Modificare contatti; • Eliminare contatti. L’amministratore, nell’ambito della gestione degli agenti può: • Visualizzare agenti; • Inserire agenti; • Modificare agenti; • Eliminare agenti. 54 Fig. 6-2. Use Case Agente L’agente, nell’ambito della gestione degli appuntamenti propri può: • Visualizzare appuntamenti; • Inserire appuntamenti; • Modificare appuntamenti; • Eliminare appuntamenti. L’agente, nell’ambito della gestione dei contatti propri può: • Visualizzare contatti; • Inserire contatti; • Modificare contatti; • Eliminare contatti. Class diagram Fig. 6-3. Class diagram 55 6.3 Linguaggio utilizzato Per lo sviluppo dell’applicazione è stato utilizzato il linguaggio Java; più precisamente si è utilizzato JBuilder X. La scelta del linguaggio è stata dettata dal particolare tipo di base di dati utilizzata (Versant), la quale, attraverso librerie dedicate, ha reso trasparente vari aspetti della programmazione tra cui la gestione delle transazioni e l’interrogazione alla base di dati stessa. 6.4 Implementazione con ORDBMS Per questo tipo di implementazione si è creata una classe denominata Database al fine di rendere più veloce (in termini di numero di righe di codice scritte) l’interrogazione della base di dati. Sviluppando con logica OR, i risultati di una query devono essere memorizzati in una struttura (tipicamente un Resultset), per cui la classe database implementa i metodi che restituiscono le strutture adatte per ogni tipo di comando SQL. Inoltre la classe Database si occupa anche di instaurare la connessione con la base di dati. Per ottenere il risultato di una query si passa una stringa (nella quale è contenuta la query stessa) al metodo getResulset(String s) della classe Database creata; questo metodo ritorna un Resultset utilizzabile per recuperare i dati. Esempio “login”: Questa query seleziona le righe della tabella TAB_UTE discriminando i campi Username e Password; se viene trovata almeno una riga (esattamente una nel nostro caso) allora si testa un campo del Resultset per differenziare l’amministratore dall’utente normale, altrimenti si evidenzia il messaggio “INVALID USERNAME/PASSWORD”. String q1 = "SELECT FL_AMM, USERNAME, PASSWORD FROM TAB_UTE WHERE USERNAME = '" + jTextField1.getText() + "' AND PASSWORD = '" + jPasswordField1.getText() + "'"; Database db = new Database(); ResultSet rs = db.getResulset(q1); try { if(!rs.next() ){ jLabel4.setText("INVALID USERNAME/PASSWORD"); } else{ jLabel4.setText(""); if(rs.getString("FL_AMM").compareTo("A") == 0){ //---AMMINISTRATORE--Frame frame11 = new Frame11(null, null, "A"); frame11.show(); } else{ //---UTENTE--Frame frame11 = new Frame11(rs.getString("USERNAME"), rs.getString("PASSWORD"), "U"); frame11.show(); } } } catch(SQLException ee){ System.out.println("Errore: " + ee); } Per la gestione delle funzionalità OR estese da SQL si riporta l’esempio dell’interrogazione di tabelle con campi di tipo REF. 56 Esempio “inserimento appuntamento”: L’inserimento dell’appuntamento segue l’inserimento in TAB_DATA della data associata all’appuntamento; siccome in TAB_APP il campo DT_APP è di tipo REF, si è avuta la necessità di selezionare la reference alla riga appena inserita per poterla utilizzare nella insert dell’appuntamento; analogamente si è proceduto per la reference verso l’utente. Il problema dell’inserimento di un appuntamento con data già presente in tabella è stato risolto delegando all’applicazione il compito di visualizzare solamente le ore libere durante l’inserimento. String str_rif_ute = " (SELECT REF(U) FROM TAB_UTE U WHERE U.USERNAME = '" + userTemp + "' AND U.PASSWORD = '" + passTemp + "')"; String str_rif_dt_new = " (SELECT REF(D) FROM TAB_DATA D WHERE D.GIORNO = '" + (String)jComboBox2.getSelectedItem() + "' AND D.MESE = '" + (String)jComboBox3.getSelectedItem() + "' AND D.ANNO = '" + (String)jComboBox4.getSelectedItem() + "' AND D.ORA = '" + (String)jComboBox1.getSelectedItem() + "')"; String str_insert_new_data = "INSERT INTO TAB_DATA VALUES ('" "'" "'" "'" + + + + (String)jComboBox2.getSelectedItem() (String)jComboBox3.getSelectedItem() (String)jComboBox4.getSelectedItem() (String)jComboBox1.getSelectedItem() Database db4 = new Database(); int l = db4.esegui(str_insert_new_data); if (l != 0){ System.out.println("insert e commit data nuova OK"); } else{ System.out.println("insert data nuova KO"); return; } String str_insert_app_new = " INSERT INTO TAB_APP VALUES ( '" + jTextField1.getText() + "' " + ", '" + jTextArea1.getText() + "' " + ", '" + jSlider1.getValue() + "' " + ", '" + jTextField2.getText() + "' " + ", " + str_rif_dt_new + " " + ", " + str_rif_ute + ")"; System.out.println(str_insert_app_new); Database db5 = new Database(); int r = db5.esegui(str_insert_app_new); if (r != 0){ System.out.println("insert app nuovo OK"); } else{ System.out.println("insert app nuovo KO"); return; } + + + + "' ," + "' ," + "' ," + "')"; 57 6.5 Implementazione con OODBMS Per questo tipo di implementazione le interrogazioni alla base di dati ad oggetti vengono effettuate attraverso l’istanziazione di una classe proprietaria di Versant (VQLQuery). Dal nome della classe si evince che il linguaggio di interrogazione non è OQL ma un tipo simile appositamente creato dalla casa produttrice Versant. La classe istanziata, a cui vengono passate la connessione e la stringa con la query di interesse, ritorna un tipo Enumeration utilizzabile per recuperare gli oggetti appartenenti al risultato dell’interrogazione. Esempio “login”: Questa query (VQLQuery) ritorna tutti gli oggetti di tipo Utente (nel nostro caso uno) discriminando i campi Username e Password. Se l’Enumeration contiene l’oggetto cercato allora, dopo un cast, si controlla l’attributo dell’oggetto che specifica il tipo d’utente così da riconoscere l’amministratore dall’utente normale. String str_conn = "select selfoid from progetto_tesi.Utente where username = '" + (jTextField1.getText()) + "' AND password = '" + (jPasswordField1.getText()) + "'"; VQLQuery q1 = new VQLQuery (Application1.getSession(), str_conn ); Enumeration e1 = q1.execute(); if(!e1.hasMoreElements()) { jLabel4 .setText("INVALID USERNAME/PASSWORD"); } else { while(e1.hasMoreElements()) { Utente u = (Utente) e1.nextElement(); if(u.get_amm()) { //---AMMINISTRATORE--jLabel4.setText(""); Frame frame11 = new Frame11(null, null, true); frame11.show(); } else { //---UTENTE GENERICO--jLabel4.setText(""); Frame frame11 = new Frame11(u.getUsername(), u.getPassword(), false); frame11.show(); } } } Esempio “inserimento appuntamento”: L’inserimento dell’appuntamento avviene dopo aver reso persistente la nuova data associata e selezionato l’utente a cui l’appuntamento afferisce. L’inserimento della data nel database avviene tramite il comando makePersistent(Object o). Infine, l’appuntamento viene inserito passandogli l’oggetto data appena creato e l’oggetto utente selezionato. String str = "select selfoid from progetto_tesi.Utente where username = '" + userTemp + "' AND password = '" + passTemp + "'"; VQLQuery q2 = new VQLQuery(Application1.getSession(), str); Enumeration e2 = q2.execute(); Utente u = (Utente) e2.nextElement(); Data dTemp = new Data( (String) jComboBox2.getSelectedItem(), (String) jComboBox3.getSelectedItem(), (String) jComboBox4.getSelectedItem(), (String) jComboBox1.getSelectedItem()); Application1.getSession().makePersistent(dTemp); Appuntamento aa = new Appuntamento(dTemp, jTextField1.getText(), jTextArea1.getText(), jSlider1.getValue(), jTextField2.getText(), u); 58 Application1.getSession().makePersistent(aa); } Application1.getSession().commit(); 6.6 Manuale utente All’avvio dell’applicazione si apre il Frame del Login che permette l’autentificazione da parte dell’utente. Fig. 6-1. Frame Login Una volta autentificato si apre il Frame principale, cioè quello dove vengono visualizzati gli appuntamenti del giorno. Fig. 6-2. Frame principale 59 Le fasce orarie occupate vengono visualizzate in rosso, mentre quelle libere rimangono in verde. Nel caso in cui si effettua l’autentificazione e non si è amministratori, a differenza della figura 6-2 , verranno nascosti il bottone per la gestione degli utenti e la combo per la selezione dell’agente. Da questo Frame si possono eseguire tutte le operazioni previste per l’applicazione. Per esempio se si scegli di modificare un appuntamento si andrà a premere il bottone “Modifica” sulla rispettiva fascia oraria. Il risultato sarà la visualizzazione del Frame relativo alla modifica dell’appuntamento: Fig. 6-3. Frame Modifica/Inserimento appuntamento Un’altra operazione possibile (solo per amministratore) è la gestione degli utenti; premendo il bottone relativo si apre il Frame: Fig. 6-4. Frame Gestione utenti 60 Intuitivamente da questo Frame si possono aggiungere, modificare ed eliminare gli utenti. Per aggiungere un nuovo utente, premendo il tasto corrispondente, si apre il Frame: Fig. 6-5. Frame Modifica/Inserimento utente La gestione dei contatti avviene in modo analogo a quella degli utenti, per cui si omette la relativa descrizione. 61 Capitolo 7 Conclusioni Il lavoro di confronto tra le due diverse tecnologie, ha portato a delle conclusioni a livello di caratteristiche oltre a conclusioni derivate dallo sviluppo dell’applicazione. Riguardo alle caratteristiche dei due tipi di tecnologie, le conclusioni si possono riassumere nei seguenti punti: Supporto per i linguaggi di programmazione Object-Oriented Oracle9i(OR): Per quanto riguarda questo prodotto, il supporto per i linguaggi OO è assicurato dal fatto che per la connessione al db si utilizza un driver ODBC; a questo punto il passaggio dei dati avviene prima dal db al driver, poi dal driver all’applicazione e viceversa. Versant(OO): A differenza di Oracle, supporta a pieno C++ e Java attraverso delle librerie dedicate; L’utilizzo di un driver non si rende necessario in quanto gli oggetti nello spazio del db sono anche oggetti nello spazio dell’applicazione. Semplicità d’uso(DBMS) Oracle9i: Per quanto riguarda questo prodotto, la semplicità d’utilizzo è data dalla presenza di numerosi tools che facilitano le operazioni di gestione di un DBMS tra le quali la creazione dello schema, l’assegnamento dei ruoli e dei privilegi. Versant: Per questo prodotto, la semplicità d’utilizzo non è paragonabile a quella di Oracle9i; le operazioni di gestione sono in linea di massima più complicate da effettuare; un esempio è la creazione dello schema(Object Schema) che per questo prodotto si realizza tramite l’invocazione di un tool a riga di comando(Enhancer). Nonostante possa sembrare un problema rispetto a Oracle9i, i benefici sulla semplicità d’uso si riscontrano a livello di programmazione dove risulta più immediato usare direttamente oggetti senza forzarli(mapparli) in tabelle. Semplicità di sviluppo(applicazione) Oracle9i: Con un approccio alla programmazione Object-oriented, la semplicità di sviluppo e il ciclo di vita dell’applicazione utilizzando questo prodotto vengono migliorati in quanto Oracle9i supporta le estensioni al linguaggio SQL per la gestione degli oggetti. Versant: Con questo prodotto la semplicità di sviluppo a livello di applicazione è assicurata dal fatto che esiste una corrispondenza diretta tra l’applicazione OO e gli oggetti persistenti nel database;. Estensibilità(tipi di dati) Oracle9i: L’estensibilità nell’ambito di questo prodotto consiste nella possibilità di dichiarare nuovi tipi di dati(ADT) oltre a quelli già supportati dal sistema; In ogni caso, vari vincoli devono essere tenuti in considerazione creando sempre un compromesso tra estensibilità dei dati e funzionalità dell’applicazione. Versant: nell’ambito dell’utilizzo di questo prodotto, alle classi definite nell’applicazione corrispondono oggetti persistenti nella base di dati; questa corrispondenza semplifica notevolmente lo sviluppo in tutto il ciclo di vita dell’applicazione e migliora il suo mantenimento. Complessità delle relazioni La differenza della complessità delle relazioni si nota nel caso di relazioni non semplici(uno-auno); Oracle9i: Per quello che riguarda questo prodotto, le relazioni a livello di diagramma E-R vengono modellizzate nel db attraverso le funzionalità SQL aggiunte. Queste funzionalità permettono, tra le altre cose, di creare relazioni attraverso l’utilizzo di attributi complessi. Versant: Con questo prodotto, le relazioni sono facilitate in quanto l’utilizzo degli OID rende più navigabili relazioni complesse. 62 Maturità della tecnologia Oracle9i: La maturità della tecnologia di questo prodotto, siccome si presenta come estensione di modelli precedentemente relazionali puri, risulta ormai consolidata con una reperibilità di materiale consultativo. Versant: La maturità della tecnologia di questo prodotto si presenta non soddisfacente in quanto trovare riferimenti bibliografici non è così immediato; la scarsa maturità la si nota anche dal limitato numero di tools presenti, fatto che sta ad indicare il cauto approccio verso questa tecnologia. Esperienza dei programmatori e universalità di SQL Oracle9i: Per il prodotto in questione, l’esperienza dei programmatori si può considerare alta in quanto è da molto tempo che l’approccio Object-Oriented viene utilizzato; inoltre la compatibilità di SQL con tutti i prodotti di tipi OR assicura una familiarità con questo linguaggio. Versant: In questo prodotto, il linguaggio di interrogazione è OQL(VQL nella versione proprietaria); non esistendo una compatibilità piena tra OQL e SQL l’esperienza dei programmatori viene ridimensionata azzerando praticamente la curva di apprendimento fino ad ora sviluppata. Applicabilità Oracle9i: Per quello che riguarda l’applicabilità, la tecnologia OR si presta bene a tutte le applicazioni OO; Versant: Per le applicazioni OO non c’è dubbio che la tecnologia dei OODBMS sia migliore in quanto una mappatura trasparente è sempre preferibile ad una attuata dall’utente; in ogni caso, per il fatto che gli OR sono da più tempo sul mercato, molte organizzazioni ritengono più sicuro l’utilizzo di una tecnologia OR. Trasparenza Oracle9i: La trasparenza per questo prodotto viene a mancare in quanto è compito dello sviluppatore aggiornare le associazioni tra gli oggetti attraverso i comandi SQL. Versant: Per quanto riguarda questo prodotto, la trasparenza è ottenuta memorizzando e rintracciando gli oggetti senza una query esplicita. Un dato che si può riscontrare non dall’analisi delle tecnologie, ma dallo sviluppo dell’applicazione, è il numero delle righe di codice scritte nelle due diverse implementazioni; Questo dato deve essere preso con le dovute considerazioni; in primo luogo la complessità dell’applicazione, che non è elevata; in secondo luogo la dimensione dell’applicazione(sempre non elevata); forse è anche per questi motivi che il dato sul risparmio di codice scritto non è congruente con quello più volte incontrato in letteratura che esprimeva la riduzione di codice stesso in termini di un 30/40 %. Da una nostra analisi, il dato del risparmio è risultato circa del 12% (Fig. 7-1). 3300 3200 3100 Righe di 3000 codice Versant Oracle9i 2900 2800 2700 1 Implementazione Fig. 7-1. Numero righe di codice nelle due implementazioni 63 Le nostre impressioni sono legate alla facilità di programmazione unita all’approccio abituale che si aveva. Siamo stati quindi condizionati da una specie di stato iniziale, indotto praticamente dalla conoscenza abbastanza approfondita solo della tecnologia OR, o meglio ancora solo della tecnologia relazionale pura. Nonostante questo, il beneficio nell’utilizzo del prodotto OO, ha portato ad un approccio completamente diverso facendoci arrivare a considerare gli oggetti persistenti come oggetti già istanziati dal programma. Le modifiche alle strutture dati si sono rivelate molto più trasparenti siccome per quello che riguarda Versant una volta modificato l’oggetto di interesse, le modifiche si propagano fino al livello di persistenza. La capacità di apprendimento è risultata alta in quanto è solamente il linguaggio utilizzato ad introdurre livelli di difficoltà(nel nostro caso Java). Per concludere, l’elaborato non ha scandagliato tutti gli aspetti delle tecnologie in esame; la ragione di questo è che sono meno significativi dal punto di vista del confronto OO/OR. In ogni caso i principali argomenti solo accennati sono stati la gestione delle transazioni, la scabilità, la portabilità e l’interoperabilità. 64 Bibliografia [1] Atzeni P., Ceri S., Paraboschi S., Torlone R. Basi di dati. Milano, McGraw-Hill 1999 [2] Hamilton G., Cattell R., Fisher M. JDBC Database Access with Java. 1997 [3] Scott K. UML Explained. Milano, Addison-Wesley 2001 [4] Database Installation Guide. ORACLE. Giugno 2001 [5] Database Getting Started. ORACLE. Giugno 2001 [6] Simple Strategies for Complex Data. ORACLE. Maggio 2002 [7] Database Fundamentals Manual. VERSANT. Giugno 2003 [8] JVI Usage Manual. VERSANT. Giugno 2003 [9] Objects End-to-End The ODBMS Advantage. VERSANT. Giugno 2003 [10] Which Database is Right for the New Information Systems. VERSANT. [11] “Estensione del modello relazionale e DBMS Object Relational”, http://www.di.unico.it/∼giolito/Si202/bc07.pdf [12] “PL/SQL Object Types”, http://oracle.cis.ksu.edu/DOC/appdev.901/a89856/10_objs.htm [13] “SQL3 Object Model”, http://www.objs.com/x3h7/sql3.htm [14] “Object Database Systems”, http://www.ipipan.waw.pl/∼subieta/objbases/ObjBases.html [15] “Object Database vs. Object-Relational Databases”, http://ca.com/products/jasmine/analyst/idc/14821E.htm