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

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

Object-Relational Mapping

Esercitazione TIGA: JDBC Soluzione proposta

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

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

JDBC. Marco Tessarotto Programmazione dei Web Server Anno Accademico

JDBC versione base. Le classi/interfacce principali di JDBC

Tecnologia e Applicazioni Internet 2011/12

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

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

SQL. Laboratorio di Progettazione di Basi di Dati (CdS in Informatica e TPS)

Eccezioni. Comportamento di default (esempio) Propagazione delle eccezioni

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

JDBC di base. Le classi/interfacce principali di JDBC

Basi di dati e Web (Moduli: Laboratorio e Siti Web centrati sui Dati) Prova scritta del 14 luglio 2008

2011 Politecnico di Torino 1

Object-Relational Mapping

Esercitazione su JDBC

Progettazione del Software

Triggers. Antonella Poggi, Claudio Corona. Dipartimento di informatica e Sistemistica Università di Roma La Sapienza

Basi di Dati Esercitazione JDBC. Giugno 2007

JDBC: Introduzione. Java Database Connectivity (JDBC): parte 1. Schema dei legami tra le classi principali. Principali classi/interfacce di JDBC

Transazioni. Transazioni. Stati di una transazione. Transazioni

Soluzione esercitazione 01

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

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

JDBC: SQL nei linguaggi di programmazione

Programmazione a oggetti

Laboratorio di Basi di Dati

Transazioni. Antonella Poggi. Dipartimento di informatica e Sistemistica Università di Roma La Sapienza

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

Introduzione. 8- Programmazione di una base di dati attraverso JDBC ESEMPIO

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

Connessione ad una fonte di dati ODBC

Non si deve fare ALCUN riferimento alla parte specifica di JDBC.

Cap. 1-I 1 I sistemi informatici

Prova d Esame Compito A

Ogni ufficio è formato da 100 dipendenti, i quali hanno a loro volta 3 clienti ciascuno. Inoltre, ad ogni ufficio sono stati assegnati 4 fornitori.

GESTIONE DEGLI ERRORI

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

CORSO DI ALGORITMI E PROGRAMMAZIONE. JDBC Java DataBase Connectivity

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

Capitolo 5. Soluzione: Soluzione in C:

Le basi del linguaggio Java

OBIETTIVI DELL'ESERCITAZIONE

Universita di Milano Bicocca Corso di Basi di dati 1 in elearning C. Batini 6. SQL DDL 6.2 Data Description Language - 2

Silvia Chiusano, Paolo Garza 1

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

Unità D3. Sicurezza nelle basi di dati. Sicurezza e concorrenza nelle basi di dati. Controllo accesso. Protezione e integrità dati

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

Laboratorio di Basi di Dati

Uguaglianza e copia di oggetti

Manuale SQL. Manuale SQL - 1 -

SQL e applicazioni. Capitolo 8. SQL e linguaggi di programmazione. Applicazioni ed SQL: architettura. Una difficoltà importante.

Java: la libreria delle classi

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

Introduzione JDBC interfaccia java.sql driver caricare i driver

Siti web centrati sui dati Architettura MVC-2: i JavaBeans

I.I.S. G. COSSALI - ORZINUOVI DATABASE. Marzo 2017 Prof. Dario Tomasoni 1

Programmazione II Compitino (Vers. B)

9 - Array. Programmazione e analisi di dati Modulo A: Programmazione in Java. Paolo Milazzo

Ereditarietà (ultima)

DBMS ed Applicazioni Motivazioni

Basi di Dati: Corso di laboratorio

JDBC per l accesso Java a DB. Tito Flagella tito@link.it

Viste in MySQL e accesso a DB da Java

JDBC. Dispense per il corso di Ingegneria del Web

Architettura MVC-2: i JavaBeans

Basi di Dati CREAZIONE E POPOLAMENTO DI UNA BASE DI DATI

Esercitazione n 2. Obiettivi

Connessione con MySQL

IL LINGUAGGIO JAVA Input, Tipi Elementari e Istruzione Condizionale

RETI DI CALCOLATORI Linguaggio Java: Eccezioni

Tecnologie Web T Gestione delle Transazioni e Livelli di Isolamento

1. MyAir. Analizzare anche i criteri funzionali

PROVA FINALE Ingegneria del software

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

Introduzione Generics Iteratori. Collezioni in Java. Dr. Giulio Pellitta. 13 aprile 2011

Programmazione Java Avanzata Spring - JDBC

JDBC. A. Bechini Accesso a DataD con Java

Le basi di dati. Le basi di dati. dalla teoria all'utilizzo di tutti i giorni. Alessandro Tanasi

Programmazione. Cognome... Nome... Matricola... Prova scritta del 22 settembre Negli esercizi proposti si utilizzano le seguenti classi:

TIPI PRIMITIVI E CLASSI WRAPPER

Laboratorio di Basi di Dati Esercizio 8.4/9.1

Breve guida a PostgreSQL (versione per Windows) Gianluca Cima

Basi di Dati: Corso di laboratorio

SQL quick reference. piccolo manuale di riferimento dei principali comandi SQL (prof. Claudio Maccherani, Perugia, 2013)

FONDAMENTI DI INFORMATICA C Linguaggio Java: Eccezioni

Le eccezioni in Java

Esercitazione 11. Liste semplici

Caricamento della classe driver. Apertura della connessione. DriverManager.getConnection() Creazione di uno statement

JDBC: SQL NEI LINGUAGGI DI PROGRAMMAZIONE

Progettazione di Sistemi Informatici

Programmazione ad Oggetti. Java Parte II

Programmazione. Cognome... Nome... Matricola... Prova scritta del 11 luglio 2014

SQL e linguaggi di programmazione Applicazioni ed SQL: architettura

Corso di Laurea in Ingegneria Gestionale Esame di Informatica - a.a. 2016/ Giugno 2017

INTRODUZIONE INTRODUZIONE TABELLE HASH FUNZIONE HASH

Introduzione a MySQL. Definizione SQL. Esempio

Caratteristiche dei linguaggi per Database

Transcript:

Data Access Objects ORM: Approccio DAO Antonella Poggi Domenico Lembo Dipartimento di informatica e Sistemistica Università di Roma La Sapienza Progetto di Applicazioni Software Anno accademico 2009-2010 Data Access Objects/Persistence classes gestiscono l accesso ai dati, incapsulando le modalità di comunicazione con il DB Le Data Access Objects si fanno carico di gestire il codice SQL, mentre tutto ciò è trasparente rispetto alle corrispondenti classi di dominio e di controllo 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 applicazione, e non possiamo mai utilizzare i metodi di accesso diretto alla base dati forniti dai DAO A. Poggi, D. Lembo 1 ORM DAO pattern 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 ORM DAO: 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). A. Poggi, D. Lembo 2 A. Poggi, D. Lembo 3

ORM DAO: Definire le classi di dominio persistenti (cont.) Ciascuna classe di dominio 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 leggidatidadb) per consentire la realizzazione di politiche di lazy read. ORM DAO Data Control Services Nel caso di funzionalità coinvolgono più di una classe di dominio, o che comunque non sono realizzabili attraverso operazioni di classi di dominio (ma piuttosto come casi d uso), per l accesso ai dati si definiscono nuove classi in cui si raggruppano metodi in maniera omogenea Chiameremo queste classi Data Control Services (DCS) Le classi DCS sono sempre parte delle persistence classes Questo approccio generalizza l approccio classico DAO che non prevede l uso di Data Control Services A. Poggi, D. Lembo 4 A. Poggi, D. Lembo 5 ORM DAO DCS (cont.) ORM: Architettura con DAO e DCS 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. I Data Access Objects sono accessibili solo tramite le Domain Classes, mentre i Data Control Services sono accessibili solo tramite Controller (o al più anche Domain Classes) User Interface Classes Controller Classes Domain Classes Persistence Classes DAO/DAC Data Store Utility Classes A. Poggi, D. Lembo 6 A. Poggi, D. Lembo 7

ORM: DAO e DCS esempio Studente ha responsabilità sull associazione iscrizione La classe DAO corrispondente a Studente conterrà i metodi necessari per l inserimento nella base dati di uno studente, gestendo anche l inserimento della facoltà a cui è iscritto una classe DAO può accedere di fatto a tutto il DB e non solo alla tabella che intuitivamente corrisponde alla classe studente! Se invece voglio ottenere la lista di tutti gli studenti, la query SQL necessaria per l estrazione dei dati necessari va inserita in un apposito metodo contenuto in una classe DCS (perchè è una informazione che non ha senso chiedere ad un oggetto di classe studente, né ad un oggetto istanza di un altra classe). A. Poggi, D. Lembo 8 A. Poggi, D. Lembo 9 Coffee name : stringa; sup_id: intero; price: reale; sales: intero; total: intero; leggidatidadb(); inseriscidatisudb(); aggiornadatisudb(); cancelladatidadb(); 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; A. Poggi, D. Lembo 10 A. Poggi, D. Lembo 11

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.(this); public void cancelladatidadb() throws Exception {CoffeeDAO.delete(this); A. Poggi, D. Lembo 12 A. Poggi, D. Lembo 13 public void aggiornaprezzosudb() throws Exception {CoffeeDAO.Price(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 tostring() public String tostring() { return "cofname: "+this.name+" sup_id: "+this.sup_id+ " price: "+this.price+" sales: "+this.sales+ " total: "+total; //Ridefinizione dell operatore di hashing public int hashcode() { return name.hashcode(); A. Poggi, D. Lembo 14 A. Poggi, D. Lembo 15

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 ) 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. */ public class CoffeeDAO { private CoffeeDAO() { A. Poggi, D. Lembo 16 A. Poggi, D. Lembo 17 // Comando SQL per l inserimento di una nuova istanza private static final String INSERT_SQL = "INSERT INTO COFFEE 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 A. Poggi, D. Lembo 18 //Comando SQL per l ottenimento di una nuova istanza private static final String FIND_BY_NAME = "SELECT * FROM COFFEE 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(); A. Poggi, D. Lembo 19

//Comando SQL per l aggiornamento di una istanza private static final String UPDATE_BY_NAME = "UPDATE COFFEE SET"+ " SUP_ID=?,PRICE=?,SALES=?,TOTAL=? WHERE COF_NAME =?"; public static void (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(); //Comando SQL per l aggiornamento del solo prezzo di una istanza private static final String UPDATE_PRICE_BY_NAME = "UPDATE COFFEE"+ " SET PRICE=? WHERE COF_NAME =?"; public static void Price(Coffee coffee) throws SQLException, ClassNotFoundException{ Connection con=null; PreparedStatement pstmt=null; ResultSet rs=null; con = ConnectionManager.getConnection(); pstmt = con.preparestatement(update_price_by_name); pstmt.setstring(5, coffee.getname()); pstmt.setdouble(1, coffee.getprice()); pstmt.executeupdate(); pstmt.close(); con.close(); //metodo lasciato per esercizio public static void delete(coffee coffee){ A. Poggi, D. Lembo 20 A. Poggi, D. Lembo 21 di classe di controllo di Domain Control Service Il seguente controller espone un metodo per effettuare la modifica del prezzo di un tipo di caffè public class CoffeeController { private Coffeecontroller(){ public void aggiornaprezzo(string nomecaffe, Double nuovoprezzo) { Coffee caffe = new Coffee(nomeCaffe); caffe.setprice(nuovoprezzo); try{ caffe.aggiornaprezzosudb(); catch(exception ex){ System.out.println("Eccezione ("+ex.getmessage()+")"); 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; A. Poggi, D. Lembo 22 A. Poggi, D. Lembo 23

//metodo ausiliario che costruisce una lista di oggetti //di tipo Coffee a partire dalle righe di un result set private static List<Coffee> processcollectionresultset(resultset rs) throws SQLException { // La collezione e una lista collegata per // ottimizzare le operazioni di inserimento LinkedList<Coffee> all = new LinkedList<Coffee>(); while (rs.next()) { all.add(objectfromcursor(rs)); return Collections.unmodifiableList(all); // la lista e restituita in modalita read-only. // A seconda delle esigenze, si puo restituire la // lista consentendone modifiche al client del metodo //Comando SQL per la funzione di ricerca //di tutte le istanze private static final String FIND_ALL_SQL ="SELECT * FROM COFFEE"; public static List<Coffee> findall() throws SQLException, ClassNotFoundException{ List<Coffee> 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; A. Poggi, D. Lembo 24 A. Poggi, D. Lembo 25 La classe di controllo // altri metodi (la cui definizione e lasciata // come esercizio) public static Coffee findbysup_id(double cof_sup_id) { public static Collection findbyprice(double cof_price) { public static Collection findbysell(int cof_sales){ public static Collection findbytotal(int cof_total){ Il controller espone anche un metodo per la stampa di tutti i caffè public class CoffeeController {... public static void stampatutti() { try {Collection<Coffee> coll = CoffeeDCS.findAll() ; Iterator<Coffee> iter = coll.iterator(); while(it.hasnext) { Coffee c = iter.next(); System.out.println(c.toString()); catch(exception ex) { System.out.println("Eccezione ("+ex.getmessage()+")"); A. Poggi, D. Lembo 26 A. Poggi, D. Lembo 27

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. Siamo 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. A. Poggi, D. Lembo 28 A. Poggi, D. Lembo 29 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 Persistence 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. La classe MyException public class MyException extends Exception { public MyException (String messaggio) { super(messaggio); 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). A. Poggi, D. Lembo 30 A. Poggi, D. Lembo 31

Nota: In tutti gli esempi che seguono, assumiamo che il livello di isolamento di default sia REPEATABLE READ public class CoffeeDCS {... public static void AggiornaTransaz(Collection<Coffee> coll) throws MyException { Connection con=null; PreparedStatement Sales=null; PreparedStatement Total=null; String String="UPDATE COFFEE "+ "SET SALES=? WHERE COF_NAME=?"; String Statement="UPDATE COFFEE "+ "SET TOTAL=TOTAL+? WHERE COF_NAME=?"; try{ con=connectionmanager.getconnection(); Sales=con.prepareStatement(String); A. Poggi, D. Lembo 32 Total=con.prepareStatement(Statement); Iterator<Coffee> iter = coll.iterator(); con.setautocommit(false); con.settransactionisolation(connection.transaction_read_committed) while (iter.hasnext()){ Coffee c = iter.next(); Sales.setInt(1,c.getSales()); Sales.setString(2,c.getName()); Sales.executeUpdate(); Total.setInt(1,c.getSales()); Total.setString(2,c.getName()); Total.executeUpdate(); con.commit(); //end while // end try catch (SQLException ex){ if (con!= null){ try{ con.rollback(); throw new MyException("Eccezione SQL: Effettuato Rollback"); A. Poggi, D. Lembo 33 catch (SQLException excep) { throw new MyException("Error: "+excep.getmessage()); //end if // end catch (ex) finally{ if (con!= null) try{ Sales.close(); Total.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[] coffees){ int len=coffees.length; //creaimo la collezione di caffe LinkedList<Coffee> all = new LinkedList<Coffee>(); 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()+")"); A. Poggi, D. Lembo 34 A. Poggi, D. Lembo 35

ISA ISA (cont.) Coffee name : stringa;... leggidatidadb();... Arabian Coffee dimension : int;... 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.load(this); public void inseriscidatisudb() throws MyException {ArabianDAO.insert(this); public void aggiornadatisudb() throws MyException {ArabianDAO.(this); public void cancelladatidadb() throws MyException {ArabianDAO.delete(this); A. Poggi, D. Lembo 36 A. Poggi, D. Lembo 37 ISA (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 COFFEE VALUES (?,?,?,?,?)"; private static final String INSERT_ARABIAN = "INSERT INTO ARABIAN VALUES (?,?)"; public static void insert(arabian ar_coffee) throws MyException{ Connection con=null; PreparedStatement pstmt1=null; PreparedStatement pstmt2=null; try{ con = ConnectionManager.getConnection(); con.setautocommit(false); con.settransactionisolation(connection.transaction_read_committed) 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()); A. Poggi, D. Lembo 38 A. Poggi, D. Lembo 39

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 A. Poggi, D. Lembo 40 A. Poggi, D. Lembo 41 ISA Nota ISA Nota (cont.) 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_committed); CoffeeDAO.insert(ar_coffe); pstmt2 = con.preparestatement(insert_arabian); pstmt2.setstring(1, ar_coffee.getname()); pstmt2.setint(2, ar_coffee.getdimension()); pstmt2.executeupdate(); con.commit();... 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. A. Poggi, D. Lembo 42 A. Poggi, D. Lembo 43

Problema fondamentale Transazioni e oggetti 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 A. Poggi, D. Lembo 44 A. Poggi, D. Lembo 45 c2: Customer c1: Customer data: CustomerData DB c2: Customer c1: Customer data: CustomerData DB read(c1) read(c1) buildselect buildselect Select Select setdatavalues Resut setdatavalues Resut delete buildupdate builddelete delete buildupdate buildupdate A. Poggi, D. Lembo 46 A. Poggi, D. Lembo 47

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), oppure se rappresentano clienti legati per mezzo di qualche vincolo 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 A. Poggi, D. Lembo 48 A. Poggi, D. Lembo 49 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 classi 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 L oggetto di tipo Customer nell esempio precedente contiene i dati sui quali è effettuato l aggiornamento. Tali dati costituiscono il contesto della transazione. A. Poggi, D. Lembo 50 A. Poggi, D. Lembo 51

Long-lived transactions Una possibile soluzione 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. 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. A. Poggi, D. Lembo 52 A. Poggi, D. Lembo 53 c1: Customer data: CustomerData read(customer) select resut setdatavalues BOT select result verify EOT acknowledgeupdate c1: Customer data: CustomerData read(customer) select resut setdatavalues BOT select result verify notifyfailure A. Poggi, D. Lembo 54 A. Poggi, D. Lembo 55

(cont.) 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) 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; A. Poggi, D. Lembo 56 A. Poggi, D. Lembo 57 (cont.) (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 si possa aggiornare solo il prezzo di un caffè). Ad esempio, public void setprice(float price){ this.oldprice = this.price; this.price = price; Riscriviamo il metodo 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 COFFEE WHERE COF_NAME =?"; //Comando SQL per l aggiornamento di una istanza private static final String UPDATE = "UPDATE COFFEE set PRICE=? WHERE NAME =?" public static void (Coffee caffe) throws MyException{ try{ con = ConnectionManager.getConnection(); con.setautocommit(false); A. Poggi, D. Lembo 58 A. Poggi, D. Lembo 59

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 throw new MyException("errore di aggiornamento"); catch (SQLException ex){ if (con!= null){ try{con.rollback(); throw new MyException("Ecezione SQL:Effettuato 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 A. Poggi, D. Lembo 60 A. Poggi, D. Lembo 61 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 A. Poggi, D. Lembo 62