Sei sulla pagina 1di 15

Sessione 9

Database

1
Database
Non soltanto in Java, ma in informatica in generale, un ruolo fondamentale è svolto dai database.

In un qualsiasi linguaggio di programmazione e, quindi anche in Java, tutti i dati con cui si lavora sono salvati nella memoria
RAM del processore. Ciò implica che appena il programma si chiude, o appena viene spento il computer su cui gira, il valore dei
dati viene perso. Quando si ha necessità di salvare in modo permanente un dato, si deve scrivere il dato su di un’area di memoria
permanente del processore (come un hard-disk). Ciò, tipicamente, può essere fatto in due modi: salvando il dato su file o
salvando il dato su database.

Un database è quindi essenzialmente un’area di memoria permanente dedicata al salvataggio dei dati che devono sopravvivere
alla durata di un programma, come lo è anche un file sul file system. Il grande valore aggiunto dei database, rispetto ai normali
file su file system, è che in realtà i database sono dei veri e propri sistemi software in grado di garantire una serie di funzionalità
aggiuntive che i normali file non hanno, tanto che ad essere precisi si parla di DBMS, ovvero Database Management System.

2
Non è obiettivo di questo corso dilungarsi troppo sui database, ma giusto a titolo informativo osserviamo che un DBMS offre una
serie di funzioni che devono rispettare il paradigma “ACID”, acronimo di:

• A: atomicità, concetto legato alla gestione delle transazioni. Tutte le operazioni che si effettuano in una transazione su db
devono essere eseguite secondo il modello “o tutte, o nessuna”. Si pensi alla classica transazione che preleva soldi da un
conto corrente e li versa in un altro conto corrente. Al livello database ci deve essere un’unica transazione atomica che
garantisca o il successo di entrambe le operazioni (commit) o l’annullamento di entrambe (rollback)

• C: coerenza, ovvero il database deve trovarsi in uno stato coerente quando inizia e quando termina la transazione, ovvero
non devono essere violati i vincoli di integrità tra i dati presenti nel db

• I: isolamento, ovvero ogni transazione non deve interferire (deve essere isolata) con eventuali altre transazioni in
esecuzione nel DBMS

• D: durabilità (o persistenza), ovvero il DBMS deve garantire che le modifiche effettuate al termine del commit di una
transazione siano memorizzate in modo permanente

Ci sono poi un’infinità di funzioni ausiliare che tutti i moderni DBMS offrono e su cui non ci soffermeremo, ed esistono anche
diverse tipologie di database (relazionali, ad oggetti etc.); si pensi che esistono interi volumi e corsi universitari dedicati
esclusivamente ai DBMS.

3
Qui noi accenneremo solo brevemente ai database relazionali, RDBMS (la R sta per relational), descrivendo le operazioni più
elementari che si possono fare su questo tipo di database, e soprattutto le operazioni che sono per noi importanti al fine di
comprendere il resto degli argomenti trattati nel corso.

Tra i principali R-DBMS relazionali in cui un programmatore Java può imbattersi citiamo:

• Oracle: probabilmente il più evoluto e completo R-DBMS ad oggi esistente. Si tratta però di un prodotto a pagamento con
costi di licenza mediamente piuttosto elevati

• MySql: R-DBMS open-source che dispone di una versione free (community edition) ed una versione a pagamento
(enterprise edition). Attualmente di proprietà della stessa Oracle (dopo l’acquisto della Sun da parte della Oracle)

• PostgreSql: R-DBMS completamente open source e free. Ad oggi è forse la versione preferita nel mondo delle community
open source

• Microsoft Sql-Server: R-DBMS di Microsoft a pagamento

Nel corso useremo PostgreSql, essendo quest’ultimo completamente free ed open-source. PostgreSql è scaricabile gratuitamente
dall’url http://www.postgresql.org/download/.

4
In sostanza un R-DBMS è un insieme di tabelle che possono avere relazioni reciproche. I dati all’interno delle tabelle vengono
nominati record.

Attraverso il linguaggio SQL (Structured Query Language) è possibile creare, eliminare, modificare e interrogare le tabelle
presenti nel database.

Una tabella è definita attraverso un insieme di campi ed alcuni vincoli, detti constraints.

Un campo, a sua volta, è definito specificando il nome ed il tipo di dato che può ospitare, in modo analogo a come abbiamo visto
si definisco le variabili in Java.

Un vincolo di un campo (o su più campi), detto anche constraint, specifica una condizione che deve essere rispettate per quel
campo quando si inserisce un record nella tabella. I vincoli principali che analizzeremo sono le primary key e le foreign key.

La primary key è il vincolo più importante in una tabella e stabilisce che il campo (o i campi) su cui è applicato è una chiave
primaria della tabella, ovvero i record presenti in tabella non possono avere valori duplicati per quel campo (o campi). Pertanto se
si prova ad inserire un record in tabella con un valore del campo su cui è definita la primary key già presente in tabella, il
database blocca l’operazione (principio di Consistenza delle proprietà ACID di un DBMS). Inoltre, se si effettua
un’interrogazione al database per leggere i record in tabella fornendo come parametro di ricerca il valore del campo su cui è
definita la primary key il database garantisce di restituire o nessun risultato oppure uno ed un solo record. Nella pratica quando si

5
definisce una tabella, si deve creare la primary key sul campo (o i campi) che per noi è il campo più importante della tabella che
ci consenta di identificare in modo univoco i record della tabella.

Esistono due modi per definire una primary key:

• usando una sequence: si aggiunge un campo numerico “artificialmente”, nel senso che il campo non farebbe parte della
tabella da un punto di vista logico ma è aggiunto appositamente per fare da identificativo. In pratica è un contatore che si
incrementa ad ogni inserimento di un nuovo record

• usando una chiave naturale: in questo caso non viene aggiunto nessun campo “artificialmente” e quindi si usa come
chiave primaria uno dei campi che “naturalmente” fanno parte della tabella

Tipicamente, una best-practice è quella di usare sempre delle sequence eccetto i casi di tabelle molto semplici dove siamo sicuri
che uno dei campi della tabella permette sempre di identificare univocamente i record della tabella.

Ad esempio se abbiamo una tabella con solo due campi, nome e descrizione, e siamo sicuri che il nome ci permette sempre di
identificare univocamente i record della tabella, allora in questo caso potremmo usare una chiave naturale sul campo nome.

Un vincolo di foreign key, o chiave esterna, stabilisce che il campo (o i campi) su cui è applicato referenzia in modo univoco
un’altra tabella, detta tabella esterna, attraverso la primary key della tabella esterna. Ovvero il valore del campo su cui è applicata
la foreign key deve coincidere con la il valore della primary key della tabella esterna.

6
Facciamo subito un esempio:

immaginiamo di voler modellare su di un R-DBMS un tabella Aula ed una tabella Tipo_Aula. In Tipo_Aula andremo a
memorizzare due record che rappresentano un’aula reale ed un’aula virtuale. In Aula invece andremo a memorizzare il nome e la
descrizione di varie aule, specificando per ciascuna di esse se si tratta di un’aula fisica o di un’aula virtuale.

La tabella Tipo_Aula la vogliamo definire attraverso un campo nome ed un campo descrizione entrambi di tipo stringa
(varcharing in PostgreSql). Inoltre vogliamo che la primary key di Tipo_Aula sia il campo nome.

Ecco l’istruzione SQL per creare la tabella TipoAula:

CREATE TABLE tipo_aula


(
nome character varying(200) NOT NULL,
descrizione character varying(50),

7
CONSTRAINT tipoaula_pkey PRIMARY KEY (nome)
)
Notiamo che character varying(200) è la sintassi usata da PostgreSql per dichiarare un tipo di carattere a dimensione variabile
con un massimo di 200 caratteri; in pratica una stringa di massimo 200 caratteri.

NOT NULL è un altro vincolo, o constraint, che specifica che il campo non può essere vuoto. Ovvero se inseriamo un record nella
tabella tipo_aula non possiamo omettere il campo nome. Questo è sempre vero per un campo su cui applichiamo una primary
key.

CONSTRAINT tipoaula_pkey PRIMARY KEY (nome) è la sintassi usata da PostgreSql per definire il vincolo di chiave primaria
(di nome tipoaula_pkey nell’esempio) applicato sul campo nome.

Per eliminare, invece, una tabella si usa l’istruzione SQL DROP TABLE.

Ad esempio se volessimo eliminare (o droppare) la tabella tipo_aula appena creata:

DROP TABLE tipo_aula;

8
Adesso vediamo come creare una tabella Aula in cui vogliamo definire un campo nome, un campo descrizione ed un campo
tipoaula, tutti di tipo stringa, con una primary key sul campo nome ed una foreign key sul campo tipoaula che referenzi la tabella
tipo_aula:

CREATE TABLE aula


(
nome character varying(200) NOT NULL,
descrizione character varying(50),
tipoaula character varying(200),
CONSTRAINT aula_pkey PRIMARY KEY (nome),
CONSTRAINT fk_tipo_aula FOREIGN KEY (tipoaula)
REFERENCES tipo_aula (nome)
)
Notiamo l’istruzione CONSTRAINT fk_tipo_aula FOREIGN KEY (tipoaula) REFERENCES tipo_aula (nome) che tramite la
sintassi di PostgreSql ci permette di dichiarare un vincolo di foreign key, di nome fk_tipo_aula, sul campo tipoaula che referenzia
il campo nome della tabella tipo_aula (ovvero la primary key di tipo_aula).

9
Notiamo anche che avendo voluto definire una foreign key sul campo tipoaula siamo costretti a definire il campo in modo
identico alla primary key della tabella esterna tipo_aula, ovvero come character varying(200).

Per eliminare la tabella aula:

DROP TABLE aula;

Nota sulla DROP: quando si elimina una tabella che è referenziata tramite foreign key da un’altra tabella è necessario usare
l’opzione CASCADE altrimenti il DBMS bloccherà l’operazione di cancellazione. L’effetto sarà quello di rimuovere la tabella ed
anche il vincolo di foreign key dalla tabella che referenzia quella eliminata.

Nell’esempio appena visto dopo la creazione della tabella aula la tabella tipo_aula risulta referenziata da aula tramite foreign
key. Se adesso, dopo la creazione di aula, provassimo a cancellare la tabella tipo_aula otterremmo un errore dal DBMS. In
questo caso dovremo quindi scrivere:

DROP TABLE tipo_aula CASCADE;

Vediamo adesso le istruzioni SQL per inserire record nelle tabelle appena create.

INSERT INTO tipo_aula VALUES (‘AULA_REALE’,’DESCRIZIONE AULA REALE’);

10
INSERT INTO tipo_aula VALUES (‘AULA_VIRTUALE’,’DESCRIZIONE AULA VIRTUALE’);

INSERT INTO aula VALUES (‘AULA 1’, ‘DESCRIZIONE AULA 1’,’AULA_REALE’);

INSERT INTO aula VALUES (‘AULA 2’, ‘DESCRIZIONE AULA 2’,’AULA_VIRTUALE’);

Notiamo come il valore della foreign key (il terzo campo) deve essere uguale ad una delle primary key della tabella tipo_aula. Se
ad esempio provassimo a scrivere:

INSERT INTO aula VALUES (‘AULA 2’, ‘DESCRIZIONE AULA 2’,’AULA….’);

il DBMS bloccherebbe la insert indicando che stiamo violando il vincolo di foreign key in quanto il valore AULA….. non è
presente nella tabella tipo_aula.

Con le istruzioni appena viste siamo riusciti a memorizzare su database l’aula 1 di tipo reale, e l’aula 2 di tipo virtuale.

Supponiamo adesso di voler modificare l’aula 2, per indicare che è anch’essa un’aula reale e non più virtuale:

UPDATE aula SET tipoaula=’AULA_REALE’ where nome=’AULA 2’;

Se invece volessimo eliminare l’aula AULA 2 useremmo l’istruzione SQL:

DELETE FROM aula WHERE nome=’AULA 2’

11
Nota sulla DELETE e sulle foreign key: quando si cancella un record che è referenziato attraverso una foreign key in un’altra
tabella il DBMS blocca l’operazione.

Se, ad esempio, provassimo a cancellare il record AULA_VIRTUALE dalla tabella tipo_aula otterremmo un errore dal DBMS,
poiché il record è referenziato dalla tabella aula (record con nome=’AULA 2’). Se quindi volessimo proprio cancellare il record
AULA_VIRTUALE dalla tabella tipo_aula dovremmo prima andare a cancellare il record AULA 2 dalla tabella aula (o a
modificarlo in modo da cambiare il valore della foreign key, magari facendolo puntare al tipo di aula reale come visto prima) e
successivamente potremmo cancellare il record dalla tabella tipo_aula.

Supponiamo adesso di voler interrogare la base dati per recuperare tutti i tipi di aula inseriti. L’istruzione SQL è:

SELECT * FROM tipo_aula;

questa istruzione ci restituisce una vista di tutti i record, completi dei valori di tutti i campi, della tabella tipo_aula.

Se invece volessimo recuperare solamente i nomi inseriti nella tabella tipo_aula la query SQL è:

SELECT nome FROM tipo_aula;

Se adesso volessimo recuperare solo i valori del campo descrizione, ma solo dei record che hanno nome=’AULA_VIRTUALE’:

SELECT nome,descrizione FROM tipo_aula WHERE nome='AULA_VIRTUALE';

12
Se volessimo ordinare i risultati (nel caso siano più di un solo record) si può usare la clausola ORDER BY seguita da asc o desc a
seconda se si vuole, rispettivamente, un ordinamento crescente o decrescente dei risultati (il default è asc).

Ad esempio, se volessimo ordinare tutti i tipi di aula, in base al campo descrizione in ordine crescente:

SELECT * FROM tipo_aula order by descrizione;

Se volessimo ordinare tutti i tipi di aula, in base al campo descrizione in ordine decrescente:

SELECT * FROM tipo_aula order by descrizione desc;

Analogamente, le stesse considerazioni valgono per le query sulla tabella aula.

Per leggere tutti i record, comprensivi dei valori di tutti i campi, della tabella aula:

SELECT * FROM aula;

Per leggere solo il campo nome di tutti i record della tabella aula:

SELECT nome FROM aula;

Se invece volessimo leggere i campi nome e descrizione di tutti i record della tabella aula:

SELECT nome,descrizione FROM aula;

13
Se volessimo leggere i campi nome e descrizione soltanto dei record che hanno nome=’AULA 1’:

SELECT nome,descrizione FROM aula WHERE nome='AULA 1';

Da notare che tutte le parole chiave SQL non sono case-sensitive, mentre i valori dei dati sono sempre case-sensitive.

Negli esempi visti, le parole chiave SELECT, FROM, WHERE, INSERT, UPDATE, SET possono essere scritti sia in maiuscolo
che in minuscolo (non sono case-sensitive); invece, quando scriviamo nome=’AULA 1’, il valore della stringa ‘AULA 1’ è case-
sensitive (si provi a cambiare la stringa in nome=’aula 1’ per vedere la differenza).

Un’ultima istruzione SQL che vogliamo mostrare in questa fase del corso è la INNER JOIN.

Quando due tabelle sono relazionate tramite un vincolo di foreign key, come negli esempi aula e tipo_aula, è possibile effettuare
una query su di una vista unificata delle due tabelle tramite la join delle due tabelle.

Supponiamo, ad esempio, di voler recuperare tramite un’unica query tutti i record, e tutti i campi, delle tabelle aula e tipo_aula:

SELECT * FROM aula a, tipo_aula b WHERE a.tipoaula=b.nome;

Tramite il carattere “,” (virgola) viene effettuato il join delle due tabelle, ovvero vengono uniti i record delle due tabelle che
hanno il valore del campo tipoaula della tabella aula uguale al valore del campo nome della tabella tipo_aula.

14
I nomi simbolici a e b sono detti alias. Da notare come tramite l’utilizzo degli alias la sintassi per accedere ai campi delle due
tabelle diventi alias.nomeCampo.

Se volessimo selezionare soltanto alcuni campi dalla vista unificata tramite la join, possiamo usare gli alias, ovvero dei nomi
simbolici che servono a distinguere le due tabelle:

SELECT a.nome, b.descrizione FROM aula a, tipo_aula b WHERE a.tipoaula=b.nome;

15