Incapusulamento dei dati & Object Relational Mapping (ORM)

Documenti analoghi
ORM: Approccio DAO. Data Access Objects. ORM DAO pattern. ORM DAO: Definire le classi di dominio persistenti

ORM: Approccio DAO. Versione preliminare. Antonella Poggi. Dipartimento di informatica e Sistemistica Sapienza Università di Roma

Object-Relational Mapping

Object-Relational Mapping

SOLUZIONE. Requisiti. Requisiti (cont.) Requisiti (cont.) Sapienza - Università di Roma Facoltà di Ingegneria

Tecnologie di Sviluppo per il Web

Requisiti. Requisiti (cont.) Sapienza - Università di Roma Facoltà di Ingegneria

Progettazione del Software

JDBC. Marco Tessarotto Programmazione dei Web Server Anno Accademico

Meccanismi per la Gestione efficiente di transazioni in un DBMS

SOLUZIONE. Requisiti. Requisiti (cont.) Requisiti (cont.)

Progettazione del Software

Progettazione del Software

Accesso alle Basi di Dati

SQL e linguaggi di programmazione. Cursori. Cursori. L interazione con l ambiente SQL può avvenire in 3 modi:

Progettazione del Software

SAPIENZA Università di Roma Facoltà di Ingegneria dell Informazione, Informatica e Statistica

ESERCITAZIONE: AZIENDA

Verso l architettura MVC-2 i JavaBeans

Esercitazione TIGA: JDBC Soluzione proposta

Sistemi di Elaborazione dell Informazione

Esercitazione TIGA: JDBC Soluzione proposta

JDBC. Paolo Atzeni. 11 marzo Progettazione di applicazioni, una premessa

La fase di progetto e realizzazione. PROGETTAZIONE DEL SOFTWARE (Ing. Gestionale) Diagramma delle classi realizzativo

Corso di. Basi di Dati I. 9. Esercitazioni in SQL: Check, asserzioni, viste

Luca Cabibbo. Persistenza di oggetti

Sistemi informativi e basi di dati. Il modello relazionale. SQL come DCL Utilizzo di un DBMS Reale. Forme normali. Basi di dati direzionali

Prof. Pagani corrado SISTEMI INFORMATIVI E DATABASE

A livello fisico, un istanza di base di dati Oracle è composta. Gubiani & Montanari Oracle Database 3

UNIVERSITA DI FIRENZE Facoltà di Ingegneria. Persistenza Applicazioni Enterprise Uso dei modelli

Progettazione del Software

Corso di Algoritmi e Strutture dati Programmazione Object- Oriented in Java (Parte I)

Corso di Progettazione del Software

18 - Vettori. Programmazione e analisi di dati Modulo A: Programmazione in Java. Paolo Milazzo

Basi di Dati Esercitazione JDBC

Ministero della Pubblica Istruzione Ufficio Scolastico Regionale per la Sicilia Direzione Generale

Introduzione D B M G

SOLUZIONE. Requisiti. Requisiti (cont.) Fase di analisi. Università di Roma La Sapienza Facoltà di Ingegneria

Gestione delle informazioni Base di dati Modello dei dati Indipendenza dei dati Accesso ai dati Vantaggi e svantaggi dei DBMS

Elena Baralis 2007 Politecnico di Torino 1

Elena Baralis 2007 Politecnico di Torino 1

Domande utili alla preparazione dell orale di Informatica all Esame di Stato

Corso integrato di Sistemi di Elaborazione. Modulo I. Prof. Crescenzio Gallo.

Basi di Dati. Esercitazione JDBC. Ing. Paolo Cappellari

Introduzione alle Basi di Dati

Basi di Dati. Concetti e Principi Generali. Maria Mirto

Come trattare il risultato di un comando SQL (relazioni) che

2011 Politecnico di Torino 1

Corso di. Basi di Dati I. 10. Esercitazioni in SQL: Complementi

Programmazione Orientata agli Oggetti in Linguaggio Java

Corso di. Basi di Dati I. 10. Esercitazioni in SQL: Complementi

CONCETTI E ARCHITETTURA DI UN SISTEMA DI BASI DI DATI

Basi di Dati. Esercitazione JDBC 28/05/2007

Elena Baralis 2007 Politecnico di Torino 1

Una breve presentazione. Basati sulla specifica EJB Sun Microsystems. Consentono di costruire applicazioni ad oggetti distribuite, utilizzando Java

Interfacce. Esempio: interfaccia I con una sola funzione g() public interface I {

Sistemi informativi D B M G. Introduzione. Introduzione alle basi di dati D B M G 2. Elena Baralis 2007 Politecnico di Torino 1

Fondamenti di Informatica e Programmazione

Strutture dati. Il che cosa e il come. F. Damiani - Alg. & Lab. 04/05

Progettazione Logica e Modello Realizzativo

Utilizzando per la connessione al database un driver di Tipo 1:

Capitolo 9. Sistemi di basi di dati Pearson Addison-Wesley. All rights reserved

Programmazione a oggetti

Prova d Esame Compito A

Programmazione Java Avanzata Programmazione Object- Oriented in Java

Servlet & JDBC A L B E R T O B E L U S S I A N N O A C C A D E M I C O / Servlet: interazione con un DBMS

Java: Definire Classi e Creare Oggetti

PRIMO MODULO. DURATA: 80 ore CONTENUTI

CORSO DI PROGRAMMAZIONE JAVA STANDARD + ENTERPRISE EDITION

Laboratorio di Basi di Dati

Cap. 1-I 1 I sistemi informatici

A. Lorenzi, A. Rizzi Java. Programmazione ad oggetti e applicazioni Android Istituto Italiano Edizioni Atlas

2011 Politecnico di Torino 1

Hibernate. Tool per Object Relational Mapping

BASI DI DATI E CONOSCENZA GESTIONE DEI DATI E DELLA CONOSCENZA PRIMO EMICORSO - BASI DI DATI. Roberto Basili a.a. 2014/15

Eccezioni. Comportamento di default (esempio) Propagazione delle eccezioni

Servlet & JDBC. Alberto Belussi. anno accademico 2008/2009

Fondamenti di Informatica A. A / 1 9

Prova d Esame Compito A

OCA JAVA 7 SE PROGRAMMER I DOCENTE: DOTT. FAUSTO DELL ANNO

Tecnologie di Sviluppo per il Web

Programmazione a Oggetti Modulo B

GESTIONE DEGLI ERRORI

Servlet & JDBC ALBERTO BELUSSI ANNO ACCADEMICO 2009/2010. Servlet: interazione con un DBMS. In Java è possibile interagire con un DBMS attraverso

GESTIONE DEGLI ERRORI

Le eccezioni in Java

Servlet & JDBC A L B E R T O B E L U S S I A N N O A C C A D E M I C O /

Prova scritta del 13 luglio 2010

PROGRAMMAZIONE DIDATTICA DI DIPARTIMENTO A.S. 2017/2018

UML Introduzione a UML Linguaggio di Modellazione Unificato. Corso di Ingegneria del Software Anno Accademico 2012/13

D B M G 2. Linguaggio SQL: costrutti avanzati. SQL per le applicazioni

Corso di Sviluppo di applicazioni Web

19 - Eccezioni. Programmazione e analisi di dati Modulo A: Programmazione in Java. Paolo Milazzo

Corso di PHP. Prerequisiti. 8.1 PHP e MySQL. Conoscenza MySQL Tecnica della programmazione

Tipi numerici esatti Valori interi o con parte decimale di lunghezza prefissata

Atzeni, Ceri, Paraboschi, Torlone Basi di dati McGraw-Hill, JDBC e applicazioni 31/05/2004

Hibernate Laboratorio

R. Orsini - A. Roncato - F. Dalla Libera

Transcript:

Incapusulamento dei dati & Object Relational Mapping (ORM) Massimo Mecella Dipartimento di Ingegneria informatica automatica e gestionale Antonio Ruberti Sapienza Università di Roma Progetto di Applicazioni Software

Applicazioni Object-Oriented (OO) e DBMS relazionali Tecnologie object-oriented, basate ad es. sull uso dei linguaggi C++, Java, etc., sono ormai consolidate Le metodologie di progettazione OO prevedono l uso di linguaggi di modellazione concettuale, come UML, che offrono un supporto fondamentale per progettare gli strati di logica dell applicazione e di interfaccia utente di un qualsiasi sistema informatico Dove mettere i dati? I dati sono memorizzati in memoria secondaria in un DBMS relazionale (organizzati in una base di dati, eventualmente ottenuta a partire da uno schema ER).

Mancata corrispondenza tra modello OO e relazionale Il binomio applicazione OO - DBMS relazionale è quindi sovente una scelta obbligata Mentre, però, il paradigma OO si è sviluppato su principi propri dell ingegneria del software, quello relazionale affonda le radici su principi matematici, e le differenze fra i due risultano considerevoli Impedance mismatch è un termine preso a prestito dall ingegneria elettrica per denotare la mancata corrispondenza tra modello OO e relazionale

Mancata corrispondenza tra modello OO e relazionale (cont.) Noi abbiamo già avuto a che fare con problemi legati all impedance mismatch: abbiamo scelto un modello concettuale ad oggetti (il modello ER) per progettare una base di dati abbiamo però dovuto indebilire la nostra iniziale rappresentazione per poter ottenere uno schema relazionale gestibile in un DBMS Il problema che ci troviamo ad affrontare ora è in un certo senso analogo, ma al tempo stesso più difficile

Mancata corrispondenza tra modello OO e relazionale (cont.) Nel realizzare un applicazione OO, infatti, non solo usiamo strumenti di progettazione ad oggetti (UML), ma scegliamo un implementazione basata sull uso di un linguaggio di programmazione OO La corrispondenza fra il class diagram UML ed il codice (ad es. Java) è quasi immediata, ma abbiamo il problema di far colloquiare l applicazione realizzata con un DBMS che parla un linguaggio diverso: il relazionale Per far quindi coesistere applicazioni ad oggetti e DBMS relazionali occorre uno sforzo sia progettuale che implementativo

Differenze fondamentali 1. Nel modello OO, le relazioni tra classi sono realizzate con associazioni, mentre nel modello relazionale sono realizzate con i valori. 2. In un oggetto, tutti i dati sono contenuti nell oggetto medesimo (coesione); i corrispondenti dati relazionali possono essere invece contenuti in più tabelle, ed occorre conoscere la struttura del DB per accedervi. 3. In un oggetto, la business logic risiede (parzialmente) nei metodi dell oggetto stesso, mentre i dati relazionali sono esclusivamente statici e la logica deve essere implementata altrove.

Differenze fondamentali (cont.) 4. Il modello relazionale non ha la possibilità di rappresentare ereditarietà e polimorfismo, concetti tipici dei modelli OO. 5. In particolare, l ereditarietà è simulata attraverso la replicazione di dati e l uso di alcuni vincoli di integrità che vengono specificati su uno schema relazionale.

Object-relational mapping Gli oggetti dell applicazione possono essere di tipo persistente: rappresentano dati memorizzati in una base di dati relazionale. Su tali oggetti invochiamo operazioni di creazione, modifica, cancellazione, con l obiettivo di effettuare letture e modifiche sulla base dati sottostante. Per fare questo abbiamo bisogno di specificare un meccanismo di corrispondenza fra l applicazione ed il database relazionale Tale meccanismo è comunemente chiamato object-relational mapping. Nello specificare tale mapping, è necessario risolvere opportunamente il problema dell impedance mismatch.

Object-Relational Mapping (cont.) Strategie possibili: 1. Forza bruta 2. Oggetti per accesso ai dati (Data Access Objects, DAO) 3. Persistence framework

Object-Relational Mapping (cont.) Forza bruta: prevede di equipaggiare le classi dell applicazione (in particolare le classi di dominio) con metodi che interagiscono direttamente con la base di dati, ovunque questo sia necessario. Il mapping è realizzato attraverso la definizione di opportuni statement SQL (eventualmente raccolti in transazioni) scritti manualmente dal programmatore. È adatto per applicazioni semplici perché non incapsula la logica di accesso al DB.

Object-Relational Mapping (cont.) DAO: prevede di realizzare uno strato dell applicazione (chiamato appunto DAO) demandato completamente a gestire la comunicazione fra l applicazione ed il DBMS. anche in questo caso il mapping è realizzato manualmente attraverso l uso di SQL. l accesso al DB viene però opportunamente incapsulato, migliorando la modularità e l interfacciamento esplicito del codice, e fornendo uno schema di riferimento in cui è possibile migliorare problemi di accoppiamento tipici dell approccio forza bruta.

Object-Relational Mapping (cont.) Persistence Framework: prevede l utilizzo di un framework predefinito (ad es. un API Java) per la gestione della persistenza. l obiettivo è liberare il programmatore quanto più possibile dalla necessità di scrivere codice SQL nella sua applicazione. il codice SQL viene generato automaticamente sulla base di informazioni di meta-livello fornite dal programmatore (ad es. all interno di file di configurazione) incapsulamento completo: il programmatore vede il DB solo quando configura il framework

Architettura dell applicazione Client User Interface Classes Controller Classes Domain Classes System Classes Persistence Classes Server Data Store

Architettura dell applicazione Persistence classes gestiscono l accesso ai dati, incapsulando le modalità di accesso Domain classes (Business Objects) Sono le classi che rappresentano i concetti pertinenti al dominio dell applicazione. Ogni classe deve incorporare il comportamento specifico delle sue istanze. Controller Classes Le classi di controllo incorporano la logica di business. Le operazioni implementate in una classe di controllo possono coinvolgere più di una domain class, persistence class, ed eventualmente altre classi di controllo.

Architettura dell applicazione User Interface Classes Sono le classi che incorporano l interfaccia offerta direttamente all utente. System Classes Le classi di sistema gestiscono l interazione dell applicazione con il sistema operativo; ovviamente, con l incapsulamento delle interazioni col sistema (ad esempio accesso a file o funzioni specifiche), si accresce la portabilità dell applicazione.

Forza bruta Incorpora la logica di accesso ai dati sul DB nelle domain classes e nelle controller classes Non è una strategia di incapsulamento È ragionevole quando l applicazione è sufficientemente semplice e si può fare a meno di uno strato di incapsulamento (persistence classes) Accoppia fortemente lo strato delle domain classes e dei controller al database Richiede che chi progetta l applicazione abbia conoscenza dettagliata del database Richiede completo controllo sul database da parte degli oggetti delle business classes

Forza bruta: esempio Persona Nome: stringa Cognome: stringa Nascita: data Coniugato: boolean Reddito: intero Aliquota(): intero Eta(): intero Open() Save()... Lavora_in 0..1 Azienda RagioneSociale: stringa PartitaIva: stringa CapitaleSociale: intero Dimensione(): stringa Aumenta(intero x) Diminuisci(intero x) Open() Save()... Nelle classi che modellano oggetti del dominio, si potrebbe avere un metodo per popolare un oggetto con i dati del DB (Open()), o per rendere persistenti i cambiamenti effettuati sull oggetto (Save()), etc.

Forza bruta (cont.) Un oggetto di una classe di business, per accedere al database (ad es. attraverso driver JDBC): costruisce da sé lo statement SQL necessario lo passa al driver; riceve i risultati dal database; li elabora opportunamente

Forza bruta (cont.) Leggere un singolo oggetto dal DB customer: Customer buildselect Select Resut setdatavalues

Forza bruta (cont.) Eseguire una query sul DB Persona Nome: stringa Reddito: intero...... Open() Save() findbyreddito(r:intero): Collection <Persona>... Lavora_in 0..1 Azienda RagioneSociale: stringa PartitaIva: stringa CapitaleSociale: intero... findbylavorain(p:persona): Azienda

Forza bruta (cont.) Si noti che: I metodi possono essere statici Vengono restituite collezioni di oggetti, tranne che nel caso di ricerca per chiave Esiste un forte accoppiamento tra le due classi indotto dalle ricerche di Azienda per Lavora in (si possono facilmente pensare altri esempi).

Forza bruta - accoppiamento Potremmo evitare l accoppiamento? Dovremmo eliminare il metodo findbylavorain dalla classe Azienda Dovremmo definire un metodo statico findallpersona():collection<persona> all interno della classe Persona Dovremmo definire un metodo statico findbylavorain(p:persona,c:collection<persona>):azienda cliente delle classi precedenti che restituisce l azienda in cui lavora una persona La soluzione proposta è irragionevole perché in generale estremamente inefficiente. Se infatti sappiamo che ci sono due tabelle Azienda e Persona nel database sottostante, possiamo più efficientemente risolvere il problema con un semplice statement SQL.

Forza Bruta Questo approccio è da evitare. Infatti, in tal modo si offre un canale diretto dalla logica dell applicazione al DB, che viola così: interfacciamento esplicito: non è definito in maniera chiara il passaggio di informazione fra l applicazione ed il DB information hiding: non nascondo all applicazione i dettagli della base dati

Data Access Objects (DAO) Un oggetto di una classe di business, per accedere al database invoca metodi di una classe demandata a gestire gli accessi al DB (relativamente alle informazioni richieste) è questa classe che costruisce lo statement SQL e lo passa al driver, riceve i risultati dal database e li inoltra alla classe di business che la ha interrogata la classe di business effettua poi le sue elaborazioni sui dati ricevuti

Data Access Objects (DAO) Leggere un singolo oggetto dal DB customer: Customer data: CustomerData read(customer) getkeyvalues keyvalues buildselect Select setdatavalues Resut

DAO (cont.) Tutta la logica di accesso al DB è completamente incapsulata nelle Data Access Classes Cambiamenti del DB influenzano solo le Data Access Classes La classe CustomerData si fa carico di gestire il codice SQL, mentre tutto ciò è trasparente rispetto alla classe Customer (che è una domain class), la quale invoca metodi indipendenti dal DB L approccio tipico per le operazioni che coinvolgono una sola entità è quello di avere un DAO per ciascuna domain class

DAO interrogare il DB Come raggruppare le operazioni sul db? Cioè, quante classi DAO realizziamo in base alle operazioni da implementare? Si crea una classe DAO per ogni classe che rappresenta entità del dominio di applicazione Questa classe DAO conterrà i metodi di interrogazione e manipolazione della corrispondente classe di dominio In particolare conterrà le funzionalità di base: CRUD Create Read Update Delete

Data Control Services Nel caso di interrogazioni (o altre operazioni) che coinvolgano più di una classe di dominio, si definiscono nuove classi di accesso ai dati in cui queste operazioni siano raggruppate in maniera omogenea Chiameremo queste classi Data Control Services (DCS) Esse sono sempre parte delle persistence classes

Data Control Services L uso di DCS è una variante all approccio DAO puro che prevede che i controller interagiscano esclusivamente con le Data Access Classes associate alle classi di dominio. Tale variante permette di avere un codice più snello ed efficiente nel caso in cui molte operazioni richiedano il coinvolgimento di più classi di dominio. Inoltre consente di diminuire l accoppiamento fra le classi di dominio ed aumentarne la coesione. Nota: Nell approccio DAO descritto non consideriamo politiche di caching degli oggetti che vanno oltre gli scopi di questo corso.

Pratiche utili Restituire sempre una collezione di oggetti (approccio efficace e uniforme) Lazy read Quando si restituisce una collezione di oggetti, non è necessario inizializzarli con tutti i dati ad essi relativi, a meno che non si sia sicuri che l operazione richieda la lettura di tutti i dati. In genere, si scrive negli oggetti solo lo stretto necessario per identificarli nel DB, e il resto su richiesta.

Oggetti di secondo livello Quando carichiamo degli oggetti dal DBMS, di solito non carichiamo gli oggetti ad essi associati mediante una associazione. La scelta di caricare in anticipo gli oggetti collegati può essere una utile strategia di ottimizzazione, se si è certi che tali oggetti devono essere caricati immediatamente dopo.

Persistence framework Un persistence framework (ad es. EJB 3.0 e Hibernate per Java; NDO.Net Data Objects per.net) incapsula pienamente la logica di accesso al DB Dei meta-dati rappresentano la corrispondenza tra domain classes e tabelle, nonché le associazioni tra le domain classes

Persistence framework (cont.) Offrono funzionalità di base (CRUD) altre funzionalità: transazioni gestione della concorrenza caching

Persistence Framework (cont.) customer: Customer p: PersistenceFramework map:mappingclass read(customer) getkeyvalues keyvalues getmaps maps buildselect setdatavalues Select Resut

Riepilogo Forza bruta Vantaggi: semplicità rapidità di sviluppo possibilità di accedere a DB mal progettati o pre-esistenti all applicazione

Riepilogo (cont.) Forza bruta Svantaggi: accoppiamento diretto tra applicazione e DB chi sviluppa l applicazione deve conoscere dettagli sul DB difficile modifica del DB difficile riuso dell applicazione

Riepilogo (cont.) DAO Vantaggi: incapsulamento possibilità di accedere a DB mal progettati o pre-esistenti all applicazione facilità di riuso dell applicazione minore accoppiamento fra le classi di dominio (solo dovuto alle dipendenze esplicitate dal class diagram)

Riepilogo (cont.) DAO Svantaggi: c è ancora accoppiamento tra persistence classes e DB chi sviluppa l applicazione (le persistence classes) deve conoscere dettagli sul DB può essere dipendente dalla tecnologia

Riepilogo (cont.) Persistence Framework Vantaggi: chi sviluppa l applicazione non deve conoscere dettagli sul DB Facilità di cambiare la corrispondenza tra oggetti e DB (in seguito a scelte dovute alle prestazioni) facilità di riuso dell applicazione e anche del persistence framework

Riepilogo (cont.) Persistence Framework Svantaggi: difficoltà di accedere a DB mal progettati oppure pre-esistenti all applicazione decremento delle prestazioni (specie se il framework non è costruito con accortezza) può essere dipendente dalla tecnologia

Per il progetto da realizzare Si deve realizzare il progetto di un piccolo sistema informativo, costituito da: una base di dati relazionale, interrogabile mediante SQL una applicazione Java che si interfaccia alla base di dati attraverso JDBC L approccio con DAO è quello richiesto facilità di implementazione (rispetto ad un Persistence Framework) offre adeguata modularizzazione migliora la qualità del progetto migliora la rapidità di sviluppo, a fronte di un modesto investimento iniziale

Architettura dell applicazione Client User Interface Classes Controller Classes Domain Classes System Classes Persistence Classes Server Data Store

Data Access Classes Le Persistence classes gestiscono l accesso ai dati, incapsulando le modalità di comunicazione con il DB Scegliamo il pattern di implementazione tramite Data Access Classes divise in Data Access Objects (DAO) e Data Control Services (DCS) Questo approccio generalizza l approccio classico DAO che non prevede l uso di Data Control Services

Data Access Classes (cont.) Cambiamenti del DB influenzano solo le Data Access Classes i Data Access Objects sono accessibili solo tramite le Domain Classes, mentre i Data Access Services sono accessibili solo tramite Controller (o al più anche Domani Classes) Le Data Access Classes si fanno carico di gestire il codice SQL, mentre tutto ciò è trasparente rispetto alle corrispondenti classi di dominio e di controllo

Data Access Classes (cont.) A livello di logica dell applicazione siamo fortemente orientati agli oggetti: ragioniamo solo in termini di Domain Objects, cioè dei concetti pertinenti al dominio dell aplicazione, e non possiamo mai utilizzare i metodi di accesso diretto alla base dati forniti dai DAO Possiamo però usare i metodi forniti dai DCS I DCS si occupano di implementare funzionalità di accesso ai dati che non riguardano singoli oggetti (ad esempio findby<something>) questo consente di sfruttare le capacità del DBMS (ad es. per interrogazioni complesse)

Nota Approcci alternativi all uso dei DCS prevedono di 1. Eseguire operazioni complesse, ad es. query, a livello di applicazione (ad es., tramite join a livello applicazione) questa soluzione è in generale inefficiente, poichè rischia di far perdere il potere espressivo di SQL

Nota 2. Definire delle ulteriori classi di dominio ( dummy o fantoccio ) che incapsulano le funzionalità non localizzabili, ciascuna associata ad una classe DAO per l accesso al DB Le classi fantoccio hanno l unico scopo di esporre ai controller metodi che non fanno altro che inovocare a loro volta i corrispondenti metodi DAO Questo modo di agire favorisce il disaccoppiamento di moduli demandati a scopi differenti, ma è in genere inefficiente e macchinoso

Data Control Services Per aumentare l efficienza dell applicazione è quindi in genere conveniente adottare un approccio meno vincolante, basato sull uso di classi DCS (ed evitare così la realizzazione di classi di dominio dummy) Questo approccio è particolarmente adatto nei casi in cui vi sono molte interrogazioni che coinvolgono più di una entità (raggruppate in gruppi omogenei, ciascuno implementato tramite una specifica classe DCS)

Data Access Classes (cont.) Ricapitolando, ogni oggetto di una classe di dominio (o business), per accedere al database invoca metodi della classe DAO corrispondente, che gestisce gli accessi al DB (relativamente all oggetto in questione) è questa classe che costruisce lo statement SQL e lo passa al driver, riceve i risultati dal database e li inoltra all oggetto di dominio che la ha interrogata la classe di dominio effettua poi le sue elaborazioni sui dati ricevuti, ed è comunque pronta per essere utilizzata dai controller

Data Access Classes (cont.) I controller possono inoltre invocare i metodi offerti dai DCS, i quali esportano funzionalità non localizzabili in nessuna classe di dominio.

Definire le classi di Dominio persistenti le classi di dominio vengono definite sulla base di un class diagram UML realizzato in fase di progettazione (si usano tecniche di specifica del codice, come quelle viste durante il corso di Progettazione del Software I) consideriamo persistenti solo quelle che modellano dati memorizzati nella base dati sottostante e che provengono da classi contenute nel class diagram UML (cioè non ci interessa rendere persistenti le classi che implementano associazioni)

Definire le classi di Dominio persistenti la classe di domino C espone i metodi set e get per operare sui campi (privati) che rappresentano le proprietà dell entità di dominio Se necessario, ridefinisce gli operatori di confronto e di generazione del codice di hashing (metodi equals e hashcode) (ad esempio in presenza di identificatori di classe significativi).

Definire le classi di Dominio persistenti Ciascuna classe di domino espone inoltre, in genere, almeno i metodi leggidatidadb, che invoca il caricamento dal DB inseriscidatisudb, che invoca l inserimento sul DB aggiornadatisudb, che invoca l aggiornamento sul DB cancelladatidadb, che invoca la cancellazione dal DB Può esporre anche più metodi di uno stesso tipo (ad es., due versioni del metodo inseriscidatisudb) per consentire la realizzazione di politiche di lazy read.

Definire la classe DAO associata Data una classe di dominio C persistente si definisce una classe CDAO associata a C che espone i metodi per gestire la persistenza degli oggetti di classe C: caricamento, inserimento, cancellazione e aggiornamento (funzionalità CRUD). Queste funzionalità corrispondono ai metodi per la gestione della persistenza con cui è equipaggiata la corrispondente classe di dominio.

Esempio Coffee name : stringa; sup_id: intero; price: reale; sales: intero; total: intero; leggidatidadb(); inseriscidatisudb(); aggiornadatisudb(); cancelladatidadb();

Esempio Costruiamo la classe di dominio Coffee come segue public class Coffee { private final String name; private int sup_id; private double price; private int sales; private int total; public Coffee(String name) { this.name = name; } public String getname(){ return name; } public int getsup_id(){ return sup_id; }

public void setsup_id(int sup_id){ this.sup_id = sup_id;} public double getprice(){ return price; } public void setprice(double price){ this.price = price;} public int getsales(){ return sales; } public void setsales(int sales){ this.sales = sales;} public int gettotal(){ return total; } public void settotal(int total){ this.total = total;}

public void leggidatidadb() throws Exception {CoffeeDAO.load(this);} public void inseriscidatisudb() throws Exception {CoffeeDAO.insert(this);} public void aggiornadatisudb() throws Exception {CoffeeDAO.update(this);} public void cancelladatidadb() throws Exception {CoffeeDAO.delete(this);} //Ridefinizione dell operatore di uguaglianza public boolean equals(object o) { if (o == null) return false; if (!(o instanceof Coffee))

} return false; Coffee o1 = (Coffee) o; return this.name.equals(o1.name); //Ridefinizione dell operatore di hashing public int hashcode() { return name.hashcode();} } //Ridefinizione dell operatore tostring() public String tostring() { return "cofname: "+this.name+" sup_id: "+this.sup_id+ " price: "+this.price+" sales: "+this.sales+ " total: "+total; }

Esempio Supponiamo ora di avere la seguente tabella relazionale COFFEE memorizzata su una base di dati CREATE TABLE COFFEE ( COF_NAME VARCHAR(32) PRIMARY KEY, SUP_ID INTEGER UNIQUE, PRICE FLOAT, SALES INTEGER, TOTAL INTEGER )

Esempio La classe DAO corrispondente alla classe Coffee può essere implementata come segue import java.util.*; import java.sql.*; / * Data Access Object per l entita Coffee. * Incapsula le funzioni ed i tipi dato necessari * per manipolare le informazioni * della base dati pertinenti a detta entita. * Si tratta di una utility class * non istanziabile. */ public class CoffeeDAO { private CoffeeDAO() {}

// Comando SQL per l inserimento di una nuova istanza private static final String INSERT_SQL = "INSERT INTO COFFEES VALUES (?,?,?,?,?)"; public static void insert(coffee coffee) throws SQLException, ClassNotFoundException{ Connection con=null; PreparedStatement pstmt=null; con = ConnectionManager.getConnection(); pstmt = con.preparestatement(insert_sql); pstmt.setstring(1, coffee.getname()); pstmt.setint(2, coffee.getsup_id()); pstmt.setdouble(3, coffee.getprice()); pstmt.setint(4, coffee.getsales()); pstmt.setint(5, coffee.gettotal()); pstmt.executeupdate(); pstmt.close(); con.close(); //si assume la modalita autocommit }

//Comando SQL per l ottenimento di una nuova istanza private static final String FIND_BY_NAME = "SELECT * FROM COFFEES WHERE COF_NAME =?"; public static void load(coffee coffee) throws SQLException, ClassNotFoundException{ Connection con=null; PreparedStatement pstmt=null; ResultSet rs=null; con = ConnectionManager.getConnection(); pstmt = con.preparestatement(find_by_name); pstmt.setstring(1, coffee.getname()); rs=pstmt.executequery(); rs.next(); coffee.setsup_id(rs.getint("sup_id") ); coffee.setprice(rs.getdouble("price") ); coffee.setsales(rs.getint("sales") ); coffee.settotal(rs.getint("total") ); rs.close(); pstmt.close(); con.close();}

//Comando SQL per l aggiornamento di una nuova istanza private static final String UPDATE_BY_NAME = "UPDATE COFFEE SET SUP_ID=?,PRICE=?,SALES=?,TOTAL=? WHERE CO public static void update(coffee coffee) throws SQLException, ClassNotFoundException{ Connection con=null; PreparedStatement pstmt=null; ResultSet rs=null; con = ConnectionManager.getConnection(); pstmt = con.preparestatement(update_by_name); pstmt.setstring(5, coffee.getname()); pstmt.setint(1, coffee.getsup_id()); pstmt.setdouble(2, coffee.getprice()); pstmt.setint(3, coffee.getsales()); pstmt.setint(4, coffee.gettotal()); pstmt.executeupdate(); pstmt.close(); con.close();} //metodo lasciato per esercizio public static void delete(string cof_name){ }

}

La classe di controllo Il seguente controller espone un metodo per effettuare la modifica del prezzo di un tipo di caffè public class CoffeeController { public void aggiornaprezzo(string nomecaffe, Double nuovopre { Coffee caffe = new Coffee(nomeCaffe); caffe.setprice(nuovoprezzo); try{ caffe.aggiornadatisudb(); } catch(exception ex){ System.out.println("Eccezione ("+ex.getmessage()+")"); } } }

Esempio definiamo una classe DCS per implementare alcune funzionalità di ricerca sulla tabella Coffee public class CoffeeDCS { private CoffeeDCS() {} //metodo ausiliario che crea un oggetto a partire //da una riga del result set private static Coffee objectfromcursor(resultset rs) throws SQLException { Coffee c = new Coffee(rs.getString(1)); c.setsup_id(rs.getint(2)); c.setprice(rs.getfloat(3)); c.setsales(rs.getint(4)); c.settotal(rs.getint(5)); return c;

} //metodo ausiliario che inserisce in una lista oggetti //di tipo Coffee costruiti dalle righe del result set private static List processcollectionresultset(resultset rs) throws SQLException { // La collezione e una lista collegata per // ottimizzare le operazioni di inserimento LinkedList all = new LinkedList(); while (rs.next()) { all.add(objectfromcursor(rs)); } return Collections.unmodifiableList(all); }

//Comando SQL per la funzione di ricerca //di tutte le istanze private static final String FIND_ALL_SQL ="SELECT * FROM COFF public static Collection findall() throws SQLException, ClassNotFoundException{ Collection all=null; Connection con=null; Statement stmt=null; ResultSet rs=null; con = ConnectionManager.getConnection(); stmt = con.createstatement(); rs = stmt.executequery(find_all_sql); all = processcollectionresultset(rs); stmt.close(); rs.close(); con.close(); return all; }

// altri metodi (la cui definizione e lasciata // come esercizio) public static void findbyprice(string cof_name) { } public static void findbysell(string cof_name){ } } public static void findbytotal(string cof_name){ }

La classe di controllo Il controller espone anche un metodo per la stampa di tutti i caffè public class CoffeeController {... public static void stampatutti() {try {Collection coll = CoffeeDCS.findAll() ; Iterator iter = coll.iterator(); while(it.hasnext) {Coffee c = (Coffee) iter.next(); System.out.println(c.toString()); } } catch(exception ex) {System.out.println("Eccezione ("+ex.getmessage()+")");} } }

Incapsulare le eccezioni Lo strato che incapsula l accesso al database (formato dalle classi DAO e DCS) rende le classi di domino ed i controller completamente indipendenti dalla base dati. Non solo in queste classi non si fa uso di comandi SQL, ma anche l API JDBC non viene utilizzata. Nei controller siamo però costretti a catturare come eccezioni di classe Exception (quindi generiche) le eccezioni generatesi nelle classi DCS e DAO, che sono di classe SQLException (e di classe ClassNotFoundException).

Incapsulare le eccezioni (cont.) Nel momento in cui gestiamo l eccezione non possiamo quindi accedere ai metodi specifici di queste classi ad es., geterrorcode(), getsqlstate()). Inoltre, l interazione con la base di dati a seguito di una eccezione (ad esempio per effettuare un rollback) può diventare macchinosa.

Incapsulare le eccezioni (cont.) Una soluzione potrebbe essere catturare le eccezioni a livello di DAO e DCS Questa soluzione implica però che a livello di Persistences Classes ci si deve occupare anche di informare l utente del tipo di eccezione verificatasi, compito invece demandato alle User Interface Classes. Per risolvere questo problema, definiamo una classe di eccezioni specifica per l applicazione. Questa classe fa parte dell insieme delle Utility Classes usate dall applicazione (classi di sistema e classi definite dal programmatore per l applicazione in questione).

La classe MyException public class MyException extends Exception { public MyException (String messaggio) { super(messaggio); } }

Esempio Nota: In tutti gli esempi ceh seguono, assumiamo che il livello di isolamento di default sia REPEATABLE READ public class CoffeeDCS {... public static void AggiornaTransaz(Collection coll) throws MyException { Connection con=null; PreparedStatement updatesales=null; PreparedStatement updatetotal=null; String updatestring="update COFFEES "+ "SET SALES=? WHERE COF_NAME=?"; String updatestatement="update COFFEES "+ "SET TOTAL=TOTAL+? WHERE COF_NAME=?";

try{ con=connectionmanager.getconnection(); updatesales=con.preparestatement(updatestring); updatetotal=con.preparestatement(updatestatement); Iterator iter = coll.iterator(); con.setautocommit(false); con.settransactionisolation(connection.transaction_read_co while (iter.hasnext()){ Coffee c = (Coffee) iter.next(); updatesales.setint(1,c.getsales()); updatesales.setstring(2,c.getname()); updatesales.executeupdate(); updatetotal.setint(1,c.getsales()); updatetotal.setstring(2,c.getname()); updatetotal.executeupdate(); con.commit(); }//end while }// end try

catch (SQLException ex){ if (con!= null){ try{ con.rollback(); } catch (SQLException excep) { throw new MyException("Error: "+excep.getmessage()) }//end if }// end catch (ex) finally{ if (con!= null) try{ updatesales.close(); updatetotal.close(); con.setautocommit(true); con.settransactionisolation(connection.repeatable_read con.close(); } catch (SQLException ex2) { throw new MyException("Error: "+excep.getmessage()); }//end finally }// end AggiornaTransaz

}//end class

La classe di controllo public class CoffeeController {... public void aggiornavendite(int[] salesforweek; String[] coffee { int len=coffees.length; //creaimo la collezione di caffe LinkedList all = new LinkedList(); for(int i=0; i<len; i++) { Coffee c = new Coffee(coffees[i]); c.setsales(salesforweek[i]); all.add(c); }//end for try { AggiornaTransaz(all); } catch(myexception ex) { System.out.println("Eccezione ("+ex.getmessage()+")"); } } }

ISA Esempio Coffee name : stringa;... leggidatidadb();... Arabian Coffee dimension : int;...

ISA Esempio (cont.) public class Arabian extends Coffee { private int dimension; public void setdimension (int dim){ this.dimension = dim; } public int getdimension(){ return dimension; } public void leggidatidadb() throws MyException {ArabianDAO.loa public void inseriscidatisudb() throws MyException {ArabianDAO.insert(this);} public void aggiornadatisudb() throws MyException {ArabianDAO.update(this);} public void cancelladatidadb() throws MyException {ArabianDAO.delete(this);} }

ISA Esempio (cont.) Assumendo che nello schema relazionale sia presente la tabella CREATE TABLE ARABIAN( NAME VARCHAR(32) PRIMARY KEY REFERENCES COFFEE(COF_NAME), DIMENSION INTEGER ) creiamo la seguente classe DAO (solo il metodo insert, il resto si lascia come esercizio) public class ArabianDAO { private ArabianDAO() {} // Comandi SQL per l inserimento di una nuova istanza private static final String INSERT_COFFEE = "INSERT INTO COFFEES VALUES (?,?,?,?,?)"; private static final String INSERT_ARABIAN = "INSERT INTO ARABIAN VALUES (?,?)";

public static void insert(arabian ar_coffee) throws MyExceptio Connection con=null; PreparedStatement pstmt1=null; PreparedStatement pstmt2=null; try{con = ConnectionManager.getConnection(); con.setautocommit(false); con.settransactionisolation(connection.transaction_read_ pstmt1 = con.preparestatement(insert_coffee); pstmt1.setstring(1, ar_coffee.getname()); pstmt1.setint(2, ar_coffee.getsup_id()); pstmt1.setdouble(3, ar_coffee.getprice()); pstmt1.setint(4, ar_coffee.getsales()); pstmt1.setint(5, ar_coffee.gettotal()); pstmt1.executeupdate(); pstmt2 = con.preparestatement(insert_arabian); pstmt2.setstring(1, ar_coffee.getname()); pstmt2.setint(2, ar_coffee.getdimension()); pstmt2.executeupdate(); con.commit(); }

catch (SQLException ex){ if (con!= null){ try{ con.rollback(); } catch (SQLException excep) { throw new MyException("Error: "+excep.getmessage()) }//end if }// end catch (ex) finally{ if (con!= null) try{ pstmt1.close(); pstmt2.close(); con.setautocommit(true); con.settransactionisolation(connection.repeatable_read con.close(); } catch (SQLException ex2) { throw new MyException("Error: "+excep.getmessage()); }//end finally }// end insert }//end class

ISA Nota Il seguente codice, che prevede di invocare il metodo insert della classe CoffeeDAO all interno nel metodo insert della classe ArabianDAO() sarebbe stato errato:... try{con = ConnectionManager.getConnection(); con.setautocommit(false); con.settransactionisolation(connection.transaction_read_comm CofeeDAO.insert(ar_coffe); pstmt2 = con.preparestatement(insert_arabian); pstmt2.setstring(1, ar_coffee.getname()); pstmt2.setint(2, ar_coffee.getdimension()); pstmt2.executeupdate(); con.commit(); }...

ISA Nota (cont.) Infatti, nel momento in cui eseguiamo il metodo CoffeeDAO.insert(ar coffe); viene iniziata una nuova transazione (se il metodo ConnectionManager.getConnection() lo consente vedi slide su JDBC). Questo fa perdere l atomicità dell operazione di inserimento di un oggetto di classe Arabian. In generale, una transazione deve essere definita completamente all interno di un metodo che risiede in una classe DAO o DCS, e non si può invocare al suo interno alcun altro metodo che accede al database. Questo infatti inizierebbe una nuova transazione.

Linee guida alla realizzazione dell applicazione: Classi di dominio Il class diagram dell applicazione fornisce indicazioni su quali classi java implementare come classi di dominio, insieme ai campi dati di ciascuna classe, ed ai metodi che ciascuna classe deve esportare Ciascuna classe java di dominio corrisponde ad una classe UML, ad una associazione UML (classe TipoLink), oppure è una classe associazione definita per gestire associazioni UML in cui la responsabilità sia assegnata a più di una classe che ha un ruolo nell associazione stessa (vedi lucidi del corso di Progettazione del Software I)

Tutte le classi java di dominio vengono racchiuse all interno di un singolo package, chiamato in genere DomainClasses I metodi di ciascuna classe si riferiscono in genere a manipolazioni di singole istanze della classe (per questo sono non-statici) Alcune di queste classi sono da considerarsi persistenti e perciò esportano anche metodi per la gestione della persistenza (inseriscidatisudb, leggidatidadb, etc.)

Transazioni e oggetti

Problema fondamentale Gli oggetti della applicazione che rappresentano dati persistenti potrebbero essere inconsistenti rispetto ai dati nel DB, o tra loro Infatti, le operazioni sugli oggetti non sono immediatamente aggiornate nel DB Occorre gestire con accortezza gli oggetti dell applicazione che rappresentano dati persistenti

Esempio c2: Customer c1: Customer data: CustomerData DB read(c1) buildselect setdatavalues Select Resut update update update update buildupdate update buildupdate update

Esempio c2: Customer c1: Customer data: CustomerData DB read(c1) buildselect setdatavalues Select Resut delete update update builddelete delete buildupdate update

Osservazioni Gli oggetti c1 e c2 aggiornano (o cancellano) i proprî dati nel DB, attraverso un oggetto che incapsula l accesso ai dati. Può accadere che i due aggiornamenti confliggano? Sì, se c1 e c2 rappresentano il medesimo cliente (Customer)

Osservazioni Abbiamo a che fare con transazioni su oggetti oltre che su dati Dobbiamo garantire che una transazione eseguita sugli oggetti venga poi trasferita in modo opportuno nel DB Possiamo avvalerci del supporto transazionale del DBMS

Osservazioni Si noti che una transazione per il DBMS può essere definita solo a livello del DAO L applicazione deve quindi suddividere il lavoro in transazioni per il DBMS che vengono innescate con opportune chiamate ai metodi delle classsi del DAO. in alcuni casi questo non è semplice, soprattutto per quelle operazioni che richiedono più accessi ai dati dipendenti da alcune scelte fatte a livello di applicazione (magari interagendo con l utente)

Concetto fondamentale Lo stato degli oggetti coinvolti in una transazione costituisce il contesto della transazione Esempio L oggetto di tipo Customer nell esempio precedente contiene i dati sui quali è effettuato l aggiornamento. Tali dati costituiscono il contesto della transazione.

Long-lived transactions Tipicamente passa un lungo intervallo di tempo tra il momento in cui inizializziamo gli oggetti leggendo dal DB e il momento in cui eseguiamo degli aggiornamenti. Dal punto di vista degli oggetti, stiamo eseguendo una transazione con vita estremamente lunga (long-lived transaction) Questo è un problema, perché se facciamo corrispondere a tale transazione una transazione sul DB (quando possibile), ciò è estremamente inefficiente, a meno che non si usi un livello di isolamento basso. Lo stesso accade se implementiamo (con fatica) un meccanismo di lock sugli oggetti dell applicazione.

Una possibile soluzione Prima di aggiornare i dati nel DB, si verifica che i dati nel DB siano consistenti con quelli letti quando l oggetto è stato inizializzato. Se questo non si verifica, si annulla l operazione. Per procedere in questo modo abbiamo bisogno di memorizzare opportunamente lo stato degli oggetti. La verifica al punto precedente e l aggiornamento successivo (se la verifica ha esito positivo) vengono effettuati all interno di una transazione del DB. Il controllo di concorrenza è affidato al DBMS, e il livello di isolamento è posto al livello minimo necessario.

Esempio c1: Customer data: CustomerData read(customer) setdatavalues select resut update update acknowledgeupdate verify BOT select result update EOT

Esempio c1: Customer data: CustomerData read(customer) setdatavalues select resut update update BOT select result notifyfailure verify

Esempio Ritorniamo all esempio del caffè, in cui non ci siamo finora occupati di controllare eventuali accessi concorrenti alla base dati In particolare, potrebbe accadere che nel momento in cui aggiorniamo il valore del caffè sulla base dati, sovrascriviamo un aggiornamento concorrente (questo è in genere indesiderabile)

Esempio (cont.) Modifichiamo la classe di dominio Coffee, aggiungendo le seguenti variabili di istanza private int oldsup_id; private float oldprice; private int oldsales; private int oldtotal;

Esempio (cont.) Inoltre, modifichiamo tutti i metodi set della classe Coffee in modo da mantenere il vecchio stato a seguito di modifiche (assumiamo per semplicità che da applicazione venga fatta una sola modifica sull oggetto prima di inviare il comando di aggiornamento al database il metodo è comunque facilmente generalizzabile). Ad esempio, public void setprice(float price){ this.oldprice = this.price; this.price = price; }

Esempio (cont.) Riscriviamo il metodo update della classe CoffeeDAO in modo che effettui il confronto con il vecchio stato memorizzato //Comando SQL per l ottenimento di una nuova istanza private static final String FIND_BY_NAME = "SELECT * FROM COFFEES WHERE COF_NAME =?"; //Comando SQL per l ottenimento di una nuova istanza private static final String UPDATE = "UPDATE COFFEE set PRICE=? WHERE NAME =?" public static void update(coffee caffe) throws MyException{ try{ con = ConnectionManager.getConnection(); con.setautocommit(false);

con.settransactionisolation( Connection.TRANSACTION_REPEATABLE_READ); pstmt = con.preparestatement(find_by_name); pstmt.setstring(1, caffe.getname()); rs=pstmt.executequery(); if (rs.next()) if (rs.getfloat(3)==caffe.getoldprice()){ pstmt = con.preparestatement(update); pstmt.setfloat(1, caffe.getprice()); pstmt.setstring(2, caffe.getname()); pstmt.executeupdate(); con.commit(); } else{ // lancia una eccezione poiche la verifica // sullo stato e fallita con.setautocommit(true); con.settransactionisolation(connection.repeatable_rea throw new MyException("errore di aggiornamento"); } }

catch (SQLException ex){ if (con!= null){ try{ con.rollback(); } catch (SQLException excep) { throw new MyException("Error: "+excep.getmessage()) }//end if }// end catch (ex) finally{ if (con!= null) try{ pstmt.close(); con.setautocommit(true); con.settransactionisolation(connection.read_commited); con.close(); } catch (SQLException ex2) { throw new MyException("Error: "+excep.getmessage()); }//end finally }// end insert }//end class

Transazioni su più oggetti Il contesto è rappresentato da un insieme di oggetti La classe di accesso ai dati dovrà avere un metodo che ha in ingresso una collezione di oggetti, e aggiorna gli oggetti di tale collezione tutti in una medesima transazione Ovviamente, ciò va fatto solo se è richiesto che l aggiornamento sia effettivamente una operazione atomica

Linee guida alla realizzazione dell applicazione: GUI e classi controller Gli Use Case individuati in fase di progettazione elencano le operazioni che l applicazione effettua sugli oggetti per rispondere alle esigenze degli utenti In semplici programmi, le operazioni di uno Use Case possono essere implementate attraverso una sola classe (cliente delle classi di dominio) che esporta metodi statici che corrispondono alle operazioni elencate. Si può comunque decidere di organizzare tali operazioni in più classi in base a diverse esigenze di modularizzazione

Si noti, in particolare, che uno Use Case può presentare anche operazioni legate alla gestione dell interfaccia grafica utilizzata per la realizzazione del caso d uso. Queste operazioni devono essere implementate in una classe diversa dalla classe in cui si implementano le reali manipolazioni sui dati Questo consente di mantenere logicamente separati gli aspetti grafici (implementati attarverso classi di Graphical User Interface (GUI) che, ad esempio, estendono classi del package java SWING) da quelli della logica di business (implementati attraverso classi controller che parlano in termini di oggetti)

Le classi GUI devono inoltre essere contenute in un package specifico (chiamato appunto GUI), mentre le classi controller vengono inserite in un package differente (detto in genere al, cioè package per l application logic) Per aumentare la modularizzazione dell applicazione è bene inoltre che le classi del package GUI interagiscano solo con classi del package al, alle quali passano dati sotto forma di tipi predefiniti (String, int, etc.), o collezioni di tipi predefiniti Il package GUI dipende quindi solo dal package al, ma non dal package DomainClasses

Linee guida alla realizzazione dell applicazione: DAO e DCS Per rispondere ad un esigenza di incapsulamento dell accesso al database, né le classi di dominio (package DomainClasses), né le classi controller (package al) comunicano direttamente con il database, cioè non fanno direttamente uso di JDBC. Si realizza quindi un ulteriore package per gestire l accesso ai dati: il package DAC (Data Access Classes) questo package conterrà a sua volta i package DAO e DCS, e la classe ConnectionManager

Le classi del package DAC fanno uso di JDBC per accedere al database (sono le uniche classi dell applicazione che usano JDBC) Il package DAO contiene una classe per ogni classe di dominio persistente. In genere non si definiscono classi DAO per le classi TipoLink Ogni classe DAO esporta metodi statici che operano aggiornamenti, cancellazioni, inserimenti o modifiche sulla base dati e prendono in input oggetti della corrispondente classe di dominio ogni classe DAC esporta metodi statici che restituiscono collezioni di oggetti istanze di classi di dominio nel caso di interrogazioni per effettuare ricerche, oppure metodi che operano modifiche sul DB sulla base di insiemi di oggetti in input.

Linee guida: errori comuni sulle classi di dominio ed i DAO Si fa presente che il class diagram dell applicazione non è in genere una copia dello schema ER realizzato per creare la base di dati ci potrebbero essere delle classi di dominio che non corrispondono ad alcuna entità dello schema ER. Queste classi non sono ovviamente persistenti, e pertanto non richiedono la realizzazione di una classe DAO associata

inoltre ci potrebbero essere associazioni nel class diagram che non corrispondono a relazioni dello schema ER, anche se le classi coinvolte nell associazione corrispondono a delle entità dello schema ER Questo avviene perchè in generale le strutture dati necessarie a livello di applicazione non coincidono semplicemente con la rappresentazione concettuale dei dati memorizzati nella base di dati

Package Diagram dell applicazione Graphical User Interface Application Logic Utility Classes MyException Domain Classes Data Access Classes Data Control Services Connection Manager Data Access Objects