Laboratorio di reti II: Gestione di database lato server Stefano Brocchi [email protected] 23 marzo, 2009 Stefano Brocchi Laboratorio di reti II: Database 23 marzo, 2009 1 / 32 Uso di database lato server Uso di database lato server Per la gestione di dati persistenti lato server vengono solitamente utilizzati database Questo offre diversi vantaggi: Operazioni sui dati altamente ottimizzate grazie a meccanismi ampiamente studiati e collaudati Disponibilità di un formato standard per i dati salvati e per la loro manipolazione Gestione automatica della concorrenza per le richieste più semplici Stefano Brocchi Laboratorio di reti II: Database 23 marzo, 2009 2 / 32 Uso di database lato server MySql Come esempio di database con cui interfacciarsi vedremo MySql MySql è uno dei più utilizzati database in rete. E’ distribuito gratuitamente nella sua versione base a www.mysql.com Le differenze di utilizzo con altri database sono comunque minime, e comprendono solitamente solo una diversa configurazione dei driver ed un’altra stringa di richiesta per la connessione Stefano Brocchi Laboratorio di reti II: Database 23 marzo, 2009 3 / 32 Uso di database lato server Database e paradigma client-server Il database serve richieste tramite il paradigma client-server Quando viene avviato, un server resta in ascolto per richieste al database Per MySql la porta di ascolto di default è 3306 Per eseguire una query quindi sarà necessario aprire una connessione tramite la quale inviare la richiesta e ricevere la risposta Grazie a questa tecnica, la località di un database (in locale o in remoto) è del tutto trasparente Stefano Brocchi Laboratorio di reti II: Database 23 marzo, 2009 4 / 32 PEAR e gestione di database in PHP PEAR e il pacchetto MDB2 PEAR (PHP Extension and Application Repository) è un insieme di pacchetti e di estensioni per PHP messi a disposizione gratuitamente online (http://pear.php.net) Uno dei pacchetti più utilizzati è MDB2 che consente l’interazione con i database Per comunicare con il DB è necessario inoltre installare l’estensione PHP con il driver relativo al database utilizzato Per MySql esistono principalmente due estenzioni: un driver MySql ed un driver MySqli che supporta anche le funzionalità più avanzate Stefano Brocchi Laboratorio di reti II: Database 23 marzo, 2009 5 / 32 PEAR e gestione di database in PHP Connessione al database Connessione al database Per eseguire delle query la prima operaziome da effettuare è importare il pacchetto e richiedere una connessione con la seguente sintassi <?php require_once("MDB2.php"); $con = & MDB2::connect("mysqli://user:password@". "host/myDatabase"); ?> Ai valori user, password, host e myDatabase vanno chiaramente sostituiti valori opportuni Il carattere ’&’ server per ottenere una referenza all’oggetto invece che una sua copia. Questo modo di procedere è raccomandato nella documentazione di PEAR Stefano Brocchi Laboratorio di reti II: Database 23 marzo, 2009 6 / 32 PEAR e gestione di database in PHP Connessione al database Connessione al database Se la connessine va a buon fine, il metodo connect restituisce un oggetto che estende MDB2 Driver Common Il tipo dell’oggetto dipende dal tipo di database richiesto e dal driver utilizzato. Nel caso di MySqli è MDB2 Driver mysqli Stefano Brocchi Laboratorio di reti II: Database 23 marzo, 2009 7 / 32 PEAR e gestione di database in PHP Gestione degli errori per MDB2 Gestione degli errori del database Se si verifica un errore, l’oggetto restituito è di tipo MDB2 Error. Per la gestione degli errori, si può utilizzare il metodo MDB2::isError che restituisce true se il suo argomento rappresenta un errore. Un esempio: <?php require_once("MDB2.php"); $con = & MDB2::connect(...); if (MDB2::isError($mdb2)) { echo $mdb2->getMessage(); } ?> Il solito meccanismo di gestione degli errori viene utilizzato per la maggior parte delle richieste al DB (per esempio anche per l’invio di query) Stefano Brocchi Laboratorio di reti II: Database 23 marzo, 2009 8 / 32 PEAR e gestione di database in PHP Esecuzione di query Esecuzione di query Per eseguire istruzioni sql che non restituiscono un risulato (INSERT, DELETE o UPDATE), si può utilizzare il metodo exec() di MDB2 Driver Common Il metodo richiede in ingresso la stringa rappresentante la dichiarazione sql Viene ritornato un intero con il numero di righe modificate se l’esecuzione ha successo, o un MDB2 Error altrimenti Come per ottenere la connesione, usare il carattere ’&’ per ottenere una referenza all’oggetto Un esempio: $modifiedRows = & $con->exec("DELETE FROM myTable"); Stefano Brocchi Laboratorio di reti II: Database 23 marzo, 2009 9 / 32 PEAR e gestione di database in PHP Esecuzione di query Esecuzione di query Per eseguire SELECT, è necessario invece utilizzare il metodo query() di MDB2 Driver Common Di nuovo, in caso di errore, viene restituito un oggetto di tipo MDB2 Error In caso di successo, l’oggetto restituito rappresenta il risultato tramite un oggetto MDB2 Result Common derivante da MDB2 Result Un esempio: $res = & $con->query("SELECT * FROM myTable"); Stefano Brocchi Laboratorio di reti II: Database 23 marzo, 2009 10 / 32 PEAR e gestione di database in PHP Gestione dei risultati Gestione dei risultati Un oggetto MDB2 Result Common contiene un puntatore ad una riga dei risultati ottenuti, inizializzato alla prima riga Tramite il metodo fetch row() viene restituita una riga del risultato, ed il puntatore passa alla successiva Se le righe sono state tutte scorse, fetch row restituisce null Stefano Brocchi Laboratorio di reti II: Database 23 marzo, 2009 11 / 32 PEAR e gestione di database in PHP Gestione dei risultati Gestione dei risultati Di default fetch row() restituisce un vettore che contiene negli indici da 0 a n - 1 il contenuto della riga Specificando come argomento la costante intera MDB2 FETCHMODE ASSOC si può ottenere un vettore associativo che ha come chiavi i nomi delle colonne Es. $row = $res->fetchRow(MDB2 FETCHMODE ASSOC); Stefano Brocchi Laboratorio di reti II: Database 23 marzo, 2009 12 / 32 PEAR e gestione di database in PHP Gestione dei risultati Gestione dei risultati Altri metodi utili a disposizione per quanto riguarda il risultato di una query: numRows(), che restituisce il numero di righe contenute nel risultato numCols(), che ritorna il numero di colonne contenute nel risultato tableInfo(), che restituisce un vettore con il nome dei campi restituiti da una SELECT Stefano Brocchi Laboratorio di reti II: Database 23 marzo, 2009 13 / 32 PEAR e gestione di database in PHP Gestione dei risultati Chiusura della connesione L’ultimo passo da fare è eseguire la disconnessione da MySql Richiamare il metodo disconnect() di MDB2 Driver Common: $con->disconnect(); Stefano Brocchi Laboratorio di reti II: Database 23 marzo, 2009 14 / 32 PEAR e gestione di database in PHP Un esempio Un esempio Per riassumere, un esempio che stampa il contenuto di una tabella: require_once("MDB2.php"); $con = & MDB2::connect(...); $res = & $con->query("SELECT * FROM myTable"); echo "<table border = 1 width=’60%’>"; $row = $res->fetchRow(); while ($row != null) { echo "<tr>"; foreach ($row as $value) { echo "<td>".$value; } $row = $res->fetchRow(); } echo "</table>"; $con->disconnect(); Stefano Brocchi Laboratorio di reti II: Database 23 marzo, 2009 15 / 32 Java Database Connectivity Java Database Connectivity Java si connette a database grazie a driver JDBC (Java Database Connectivity) Il driver è fornito gratuitamente sul sito www.MySql.com sotto forma di un file JAR Dopo aver incluso il file JAR nel classpath, il driver sarà a disposizione per eseguire richieste al database Una volta incluso il driver, Java gestisce operazioni sql tramite il pacchetto java.sql Stefano Brocchi Laboratorio di reti II: Database 23 marzo, 2009 16 / 32 Java Database Connectivity Java Database Connectivity Per interagire con i database, in Java si eseguono i seguenti passi: Caricare il driver del database in memoria. Come descritto in seguito, usare una import può dare problemi quindi è necessario ricorrere a metodi alternativi Richiedere una connesione al database ottenendo un oggetto java.sql.Connection Tramite la connessione, creare un’astrazione rappresentante una dichiarazione SQL, rappresentata da un oggetto Statement Eseguire tramite istanze di Statement le query desiderate, ed elaborare i risultati rappresentati da oggetti di tipo ResultSet Chiudere la connessione con il database e tutti gli oggetti che ne fanno uso Stefano Brocchi Laboratorio di reti II: Database 23 marzo, 2009 17 / 32 Java Database Connectivity Caricare il driver JDBC JDBC: caricare il driver Per quanto riguarda il driver JDBC per MySql, un’inclusione con una import può dare problemi E’ raccomandato che la classe venga caricata dinamicamente con la seguente sintassi: Class.forName("com.mysql.jdbc.Driver").newInstance(); Tale istruzione carica dinamicamente un’istanza del driver in memoria Stefano Brocchi Laboratorio di reti II: Database 23 marzo, 2009 18 / 32 Java Database Connectivity Aprire una connessione al server sql JDBC: richiedere una connessione A questo punto è necessario richiedere una connessione al database Utilizzare la classe java.sql.DriverManager per la gestione dei driver JDBC A disposizione il metodo statico getConnection: public static Connection getConnection(String url); La stringa url rappresenta un parametro di connessione comprensibile al driver Stefano Brocchi Laboratorio di reti II: Database 23 marzo, 2009 19 / 32 Java Database Connectivity Aprire una connessione al server sql JDBC: richiesta di connessione Per collegarsi ad un database MySql, la sintassi dell’url deve essere la seguente jdbc:mysql://host[:port]/database?user= user &password=pass Dove ai valori host, port, database, user e pass vanno sostituiti opportuni valori validi per la connessione al database Un esempio di stringa per collegarsi al database nella rete interna del dipartimento di informatica: jdbc:mysql://192.167.124.67/laboratorio reti? ... user=studente2008&password=studente2008 Stefano Brocchi Laboratorio di reti II: Database 23 marzo, 2009 20 / 32 Java Database Connectivity Aprire una connessione al server sql JDBC: classe Connection Un’istanza della classe Connection rappresenta una connessione ad un server Sql Tramite il metodo createStatement è possibile ottenere un oggetto di tipo Statement che rappresenta una dichiarazione che si può inviare al server sql Dopo aver terminato la comunicazione, è opportuno chiudere la connessione tramite il metodo close() Stefano Brocchi Laboratorio di reti II: Database 23 marzo, 2009 21 / 32 Java Database Connectivity Aprire una connessione al server sql JDBC: classe Connection Numerosi altri metodi sono present nella classe Connection principalmente per vari utilizzi: Gestione della concorrenza non limitata alla singola query (es. uso di commit e rollback) Ripetizione di query molto simili fra loro, dove cambiano solo alcuni parametri, tramite oggetti PreparedStatement Estrazione di messaggio inviati dal database (es. getWarnings()) o di metadati relativi al db (es. getMetadata()) Stefano Brocchi Laboratorio di reti II: Database 23 marzo, 2009 22 / 32 Java Database Connectivity Creare una dichiarazione Sql JDBC: classe Statement Grazie alla classe Statement è possibile inviare query al database In modo simile alle connessioni, l’oggetto va chiuso con il metodo close() al termine dell’utilizzo I principali metodi da utilizzare sono: public ResultSet executeQuery(String sql) Per eseguire delle SELECT ed ottenere un oggetto ResultSet che rappresenta il risultato ottenuto public int executeUpdate(String sql) Per eseguire delle query che non ritornano risultati (INSERT, UPDATE o DELETE). Il metodo ritorna il numero di righe modificate Stefano Brocchi Laboratorio di reti II: Database 23 marzo, 2009 23 / 32 Java Database Connectivity Creare una dichiarazione Sql JDBC: classe Statement E’ inoltre possibile utilizzare il metodo boolean execute(String sql) per dichiarazioni sql generiche Il metodo restituisce true se l’esecuzione della dichiarazione ha ottenuto dei risultati E’ possibile recuperare quindi i risultati della query tramite il metodo getResultSet() Stefano Brocchi Laboratorio di reti II: Database 23 marzo, 2009 24 / 32 Java Database Connectivity Gestire i risultati JDBC: classe ResultSet La classe ResultSet restituisce un insieme di risultati ottenuti tramite una query Questi possono venir scorsi in modo sequenziale in quanto all’interno dell’oggetto c’è un puntatore alla riga corrente, inizializzato ad immediatamente prima della prima riga Stefano Brocchi Laboratorio di reti II: Database 23 marzo, 2009 25 / 32 Java Database Connectivity Gestire i risultati JDBC: classe ResultSet Per passare alla riga successiva usare il metodo next(). Questo restituisce false se la riga corrente era l’ultima. Un esempio che scorre tutto il ResultSet: ResultSet rs = myConnection.executeQuery(query); while (rs.next()) { // Operazioni sulla riga corrente } Esistono diversi altri metodi per la minipolazione del contatore, come first(), last(), isFirst(), isLast() Stefano Brocchi Laboratorio di reti II: Database 23 marzo, 2009 26 / 32 Java Database Connectivity Gestire i risultati JDBC: classe ResultSet Per ottenere il contenuto dei vari campi della colonna, esistono dei metodi getXXX (int columnIndex) che restituiscono il contenuto del campo di indice columnIndex (da 1 a n) trasformato nel tipo Java XXX Un esempio: getInt(1) restituisce il primo elemento della riga trasformato in un intero Sono disponibili inoltre metodi getXXX (String columnName) che restituiscono il valore associato ad una determinata colonna Un esempio: getInt("key") restituisce l’elemento relativo alla colonna ”key” trasformato in un intero Stefano Brocchi Laboratorio di reti II: Database 23 marzo, 2009 27 / 32 Java Database Connectivity Gestire i risultati JDBC: classe ResultSet E’ importante assicurarsi che le trasformazioni effettuate non causino perdita di dati Questo succederebbe, per esempio, convertendo un intero a 64 bit in un intero a 32 In caso di dubbio, consultare la documentazione Java, per esempio all’indirizzo http://java.sun.com/j2se/1.3/docs/guide/ ... jdbc/getstart/mapping.html Per ottenere informazioni sul tipo di dato ritornato, come il numero di colonne, è possibile utilizzare il metodo getMetaData(); l’oggetto MetaData restituito contiene i metadati del risultato. Come per Statement e Connection, un ResultSet va chiuso tramite close() dopo l’utilizzo. Stefano Brocchi Laboratorio di reti II: Database 23 marzo, 2009 28 / 32 Java Database Connectivity Un esempio JDBC: un esempio Per riassumere, un esempio completo per una richiesta al DB Class.forName("com.mysql.jdbc.Driver").newInstance(); Connection conn = DriverManager.getConnection( "jdbc:mysql://127.0.0.1/mydatabase" + "?user=anuser&password=apassword"); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery( "SELECT nome,cognome FROM myTable"); while (rs.next()) { System.out.println(rs.getString(1) + " " + rs.getString(2)); } rs.close(); stmt.close(); conn.close(); Stefano Brocchi Laboratorio di reti II: Database 23 marzo, 2009 29 / 32 Java Database Connectivity Un esempio JDBC: un esempio All’esempio precedente, reso più breve possibile per semplicità, dovrebbero essere apportati miglioramenti sotto molti aspetti per essere utilizzato in un’applicazione reale: Assicurarsi che il driver per MySql venga caricato una sola volta Aprire e chiudere le connessioni può essere una delle operazioni più gravose di una query: quando possibile, utilizzare la stessa connessione per più query Gestire opportunamente le eccezioni ed assicurarsi che tutti gli oggetti vengano chiusi anche in caso di errore Stefano Brocchi Laboratorio di reti II: Database 23 marzo, 2009 30 / 32 Esercizio Esercizio Progettare e realizzare un opportuno database per il forum Creare una tabella per le informazioni sugli utenti, una per i thread ed una per i post E’ possibile sia inserire il contenuto dei post direttamente nel DB che salvarli in file di testo, ed inserire nel database solo i nomi dei file Stefano Brocchi Laboratorio di reti II: Database 23 marzo, 2009 31 / 32 Esercizio Esercizio Utilizzare l’accesso al database nel lavoro fatto: Controllare la validità delle informazioni di login tramite DB, sia dal codice PHP che dalla servlet Estrarre le informazioni sui thread e sui post effettuati dal database Inserire nel DB informazioni a proposito di nuovi thread e post Stefano Brocchi Laboratorio di reti II: Database 23 marzo, 2009 32 / 32