Su Java è possibile accedere ad un database MySQL tramite il protocollo JDBC, uno standard industriale indipendente dal DBMS impiegato. Il suo utilizzo permette di impostare il codice in maniera che rispetti il principio, caro al mondo Java, del Write Once, Run Anywhere: un software riutilizzabile al di là delle tecnologie e dei contesti utilizzati inizialmente.
Affinchè possa interagire con JDBC, un DBMS deve offrire appositi driver che implementino le classi generiche che lo costituiscono. MySQL mette a disposizione un connector per il linguaggio Java, chiamato Connector/J e reperibile sul sito ufficiale di MySQL. Lo si può scaricare in formato sorgente o binario, ma quest'ultima opzione risulta più comoda, dal momento che richiederà il semplice inserimento di un archivio .jar nel CLASSPATH del proprio progetto.
L'utilizzo di JDBC con MySQL verrà illustrato mediante un esempio. Ci si occuperà per prima cosa del caricamento del driver e della gestione della connessione. Successivamente si svolgeranno due operazioni: un inserimento tramite PreparedStatement
ed una query che leggerà il contenuto di una tabella.
Assumeremo di accedere ad un database con i seguenti parametri di connessione:
Parametro | Valore |
---|---|
indirizzo di rete | localhost |
porta TCP | 3306 (il valore di default) |
username | root |
password | secret |
nome del database | Contatti |
Si farà accesso ad una sola tabella, persone, la cui struttura può essere prodotta come segue:
CREATE TABLE `persone` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`nome` varchar(50) NOT NULL,
`cognome` varchar(50) NOT NULL,
`eta` tinyint(3) unsigned NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
Driver e stringa di connessione
Prima di vedere il codice in azione, è bene soffermarsi sull'esecuzione della connessione.
Una volta inserito il driver nel proprio CLASSPATH, è necessario che il codice Java ne carichi a runtime la classe principale, denominata com.mysql.jdbc.Driver
. Ecco come:
try
{
Class.forName("com.mysql.jdbc.Driver");
}
catch (ClassNotFoundException e)
{
// gestione dell'eccezione
}
Nel caso il driver non sia disponibile, verrà sollevata un'eccezione di tipo ClassNotFoundException
.
L'interazione tra il programma ed il DBMS si baserà su una connessione aperta, rappresentata da un oggetto di classe Connection
.
I parametri saranno tutti inclusi in una stringa di connessione, che nel nostro esempio sarà la seguente:
String connectionString="jdbc:mysql://localhost:3306/Contatti?user=root&password=secret";
Il suo formato ricorda quello di un indirizzo di rete. Tipicamente inizia con il prefisso jdbc
, seguito da un altro termine che indica il DBMS utilizzato. Il resto è una concatenazione dell'indirizzo del database (il formato è indirizzo_host:porta_TCP/nome_database
) e di una querystring (ciò che segue il punto interrogativo) costituita dai parametri di accesso. Indichiamo solo username e password dell'account MySQL che utilizziamo, ma la documentazione ufficiale descrive tutte le ulteriori opzioni disponibili.
La classe DriverManager
userà la stringa di connessione per aprire la connessione:
Connection connection;
...
...
connection = DriverManager.getConnection(connectionString);
È importante notare che i parametri specifici del DBMS utilizzato si evincono per lo più dalla connection string e dal driver invocato. Se si volesse riutilizzare il codice con un altro tipo di database, sarebbe sufficiente modificare soltanto queste due stringhe.
L'esempio
String connectionString = "...";
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Connection connection = null;
try {
connection = DriverManager.getConnection(connectionString);
PreparedStatement prepared = connection.prepareStatement("insert into persone (cognome, nome, eta) values (?,?,?)");
prepared.setString(1, "Marroni");
prepared.setString(2, "Enrico");
prepared.setInt(3, 55);
prepared.executeUpdate();
Statement stm = connection.createStatement();
ResultSet rs = stm.executeQuery("select * from persone");
while (rs.next()) {
System.out.println(rs.getString("cognome") + " " + rs.getString("nome") + " di anni " + rs.getInt("eta"));
}
} catch (SQLException e) {
e.printStackTrace();
} catch (Exception e) {
System.out.println(e.getMessage());
} finally {
try {
if (connection != null)
connection.close();
} catch (SQLException e) {
// gestione errore in chiusura
}
}
Dando per assodati i concetti relativi alla connessione, illustrati nel paragrafo precedente, vediamo da vicino le due operazioni eseguite sulla tabella.
Per prima cosa, dobbiamo ottenere uno Statement
dalla connessione. Questo rappresenta il comando da eseguire, e sarà configurato in primis con il comando SQL ed eventualmente con altri parametri.
Per l'inserimento, utilizziamo la modalità dei PreparedStatement
, già introdotti in una lezione precedente.
Tramite il metodo prepareStatement
, creaiamo un oggetto di questo tipo a partire da un comando SQL in cui i punti interrogativi sostituiranno i parametri da utilizzare. Si invoca poi il metodo executeUpdate
, dal momento che lo Statement mira ad apportare modifiche ai dati.
Per la query utilizziamo uno Statement comune e l'interrogazione viene formulata tramite un classico comando SELECT
. In questo caso avremo bisogno di leggere i risultati ottenuti dall'esecuzione del comando executeQuery
, raccolti in un oggetto ResultSet
.
Quest'ultimo contiene un cursore che punta ai dati presenti in un determinato record dei risultati. Per fruirne sarà necessario spostarlo da un record all'altro in base alle nostre esigenze, ed una volta posizionato leggere i valori dei campi ivi presenti.
Il nostro scopo, per semplicità, sarà quello di stampare l'intero set dei risultati; pertanto passiamo da un record al successivo tramite il metodo next()
, invocato nel ciclo while
.
Ci si può muovere anche in altre direzioni sfruttando metodi come previous
(torna al precedente), first
(spostamento al primo record) o last
(spostamento all'ultimo record).
I dati di un record potranno essere letti tramite metodi specifici per ogni tipo di dato: nel codice abbiamo utilizzato getString
e getInt
per accedere, rispettivamente, a stringhe e numeri interi. Esistono anche getFloat
e getDouble
(numeri in virgola mobile), getDate
e getTime
(per informazioni temporali) e diversi altri metodi simili, indicati nella documentazione.
Infine la connessione va chiusa. Considerando che tutte le operazioni sono state incluse in un blocco try/catch
che gestisce le eccezioni di tipo SQLException
(errori di interazione con il database), la chiusura della connessione dovrebbe essere inserita in un conseguente blocco finally
, in modo da essere eseguita in ogni caso, sia di successo che si insuccesso.
Oltre JDBC
Il protocollo JDBC permette di svolgere qualunque interazione con il DBMS. Il suo svantaggio è che si tratta di un approccio “di base”, che può richiedere la scrittura di molto codice, spesso piuttosto ripetitivo.
Esistono per questo altre soluzioni, sempre basate su JDBC, che permettono un approccio più flessibile e mantenibile per l'interazione Java-MySQL. Se ne nominano di seguito i principali:
- Spring Framework e template JDBC: il framework Spring è molto usato nella gestione di grandi progetti Java. Tra le sue numerosissime funzionalità, esso offre anche i JdbcTemplate che facilitano il lavoro del programmatore svolgendo in autonomia operazioni delicate come apertura e chiusura di connessioni e gestione delle risorse, nonché la preparazione degli Statement, la lettura di risultati e l'attivazione di transazioni;
- Hibernate e altri OR/M: il filone degli OR/M, di cui Hibernate è un illustre rappresentante, propone un approccio diverso per l'accesso ai dati, basato meno sull'invio diretto dei comandi SQL, quanto piuttosto fondato sul mapping tra sistema informativo relazionale (incluso nel database) e modello a oggetti realizzato nel programma Java;
- iBatis: un framework dedicato alla persistenza dei dati che facilita l'integrazione tra il programma e il database.