Componenti dell’API JDBC
2025 – prof. Roberto Fuligni
Connection
Rappresenta una sessione di connessione a un database specifico. Attraverso un oggetto Connection
è possibile creare statement SQL, gestire le transazioni (commit, rollback) e configurare proprietà di connessione (auto-commit, livelli di isolamento).
Aprire una connessione al database è un’operazione critica che deve sempre essere chiusa correttamente per evitare memory leak e blocchi sul database. La gestione tradizionale richiedeva try-finally
, ma con Java 7 è stato introdotto try-with-resources
, che chiude automaticamente la connessione al termine del blocco.
import java.sql.*;
public static void main(String[] args) {
String url = "jdbc:mariadb://localhost:3306/dbesempio";
String user = "root";
String password = "";
try (Connection conn = DriverManager.getConnection(url, user, password)) {
// Routine di accesso al database...
} catch (SQLException e) {
e.printStackTrace();
}
}
Statement
In JDBC, gli statement sono gli oggetti utilizzati per eseguire comandi SQL su un database. Esistono tre principali tipi di statement, ognuno con caratteristiche e casi d’uso specifici:
- Statement base, per query statiche semplici;
- PreparedStatement, per query parametrizzate e più efficienti;
- CallableStatement, per eseguire stored procedure.
Statement base
Lo Statement
base di JDBC viene utilizzato per eseguire query SQL senza parametri dinamici.
È semplice da usare ma meno efficiente quando si eseguono ripetutamente query simili.
Lo statement
base non è sicuro contro SQL injection se i dati provengono da un input dell’utente.
String query = "SELECT id, nome, saldo FROM conti";
try (Connection conn = DriverManager.getConnection(url, user, password);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(query)) {
while (rs.next()) {
int id = rs.getInt("id");
String nome = rs.getString("nome");
double saldo = rs.getDouble("saldo");
System.out.format("%-7d %-18s %11.2f%n", id, nome, saldo);
}
} catch (SQLException e) {
System.err.println("Errore di connessione al database: " + e.getMessage());
}
PreparedStatement
La classe PreparedStatement
permette di utilizzare parametri segnaposto
(indicati con il carattere ?) al posto di valori fissi. Questo migliora la
sicurezza e le prestazioni, perché il database può compilare ed eseguire la
query in modo più efficiente.
Il PreparedStatement
protegge dagli attacchi di SQL injection, dato che
i parametri vengono trattati come dati e non come parte della query. Il codice
risulta più chiaro e pulito, specialmente nel caso di query complesse.
double saldoMin = 1000.0;
String query = "SELECT id, nome, saldo FROM conti WHERE saldo >= ?";
try (Connection conn = DriverManager.getConnection(url, user, password);
PreparedStatement pstmt = conn.prepareStatement(query)) {
// Parametro per il saldo minimo
pstmt.setDouble(1, saldoMin);
try (ResultSet rs = pstmt.executeQuery()) {
System.out.format("Conti con saldo non inferiore a %.2f €:%n", saldoMin);
while (rs.next()) {
int id = rs.getInt("id");
String nome = rs.getString("nome");
double saldo = rs.getDouble("saldo");
System.out.format("%-7d %-18s %11.2f%n", id, nome, saldo);
}
}
} catch (SQLException e) {
System.err.println("Errore di connessione al database: " + e.getMessage());
}
CallableStatement
Se il database supporta le stored procedure, si può usare CallableStatement
per eseguirle.
Una stored procedure è un insieme di query SQL salvate nel database, spesso usata per migliorare
le prestazioni e centralizzare la logica di business.
Se un database contiene una stored procedure dimunisci_saldi
che riduce i saldi di tutti i conti
di un certo valore, è possibile invocare la procedura con il seguente codice:
double importo = 150.0,
String callSP = "{CALL diminuisci_saldi(?)}";
try (Connection conn = DriverManager.getConnection(url, user, password);
CallableStatement cstmt = conn.prepareCall(callSP)) {
cstmt.setDouble(1, importo);
cstmt.execute();
System.out.println("Stored procedure eseguita con successo");
} catch (SQLException e) {
System.err.println("Errore durante l'esecuzione della stored procedure: " +
e.getMessage());
}
Se la stored procedure restituisce un valore (ad esempio il numero di conti con saldo inferiore a una certa soglia), si usa un parametro di output:
double soglia = 150.0;
String callFunction = "{? = CALL conta_conti_sotto_soglia(?)}";
try (Connection conn = DriverManager.getConnection(url, user, password);
CallableStatement cstmt = conn.prepareCall(callFunction)) {
cstmt.registerOutParameter(1, java.sql.Types.INTEGER);
cstmt.setDouble(2, soglia);
cstmt.execute();
int conteggio = cstmt.getInt(1);
System.out.format("Numero di conti con saldo inferiore a %.2f €: %d%n",
soglia, conteggio);
} catch (SQLException e) {
System.err.println("Errore durante l'esecuzione della funzione: " + e.getMessage());
}
ResultSet
In JDBC, ResultSet è l’oggetto che rappresenta il risultato di una query SQL
eseguita con Statement
, PreparedStatement
o CallableStatement
.
Esso permette di leggere i dati restituiti dal database riga per riga e
colonna per colonna.
È possibile ottenere un ResultSet
eseguendo una query SELECT con il metodo executeQuery()
:
il risultato conterrà tutte le righe della tabella utenti con le colonne id
e `nome
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT id, nome FROM conti");
Il ResultSet
è una struttura simile a un cursore che parte prima della prima riga e avanza con next()
:
while (rs.next()) {
int id = rs.getInt("id");
String nome = rs.getString("nome");
System.out.println("ID: " + id + ", Nome: " + nome);
}
Di seguito sono riportati i principali metodi offerti dalla classse ResultSet
.
Metodo | Descrizione |
---|---|
next() |
Sposta il cursore alla riga successiva (ritorna false se non ci sono più righe) |
previous() |
Sposta il cursore alla riga precedente (solo se il ResultSet è scorrevole) |
first() |
Si sposta sulla prima riga |
last() |
Si sposta sull’ultima riga |
absolute(int row) |
Si sposta a una riga specifica (solo se il ResultSet è scorrevole) |
relative(int rows) |
Si sposta avanti o indietro di un certo numero di righe |
Esistono tre tipi di ResultSet
, definiti durante la creazione dello Statement
:
Statement stmt = conn.createStatement(
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);
ResultSet rs = stmt.executeQuery("SELECT id, nome FROM conti");
if (rs.last()) { // Sposta il cursore sull'ultima riga
int id = rs.getInt("id");
System.out.println("Ultimo id conto: " + id);
}
Tipo | Descrizione |
---|---|
TYPE_FORWARD_ONLY (Default) |
Permette solo di avanzare con next(), senza tornare indietro |
TYPE_SCROLL_INSENSITIVE |
Permette di scorrere avanti e indietro, ma non riflette le modifiche fatte nel database dopo la query |
TYPE_SCROLL_SENSITIVE |
Permette di scorrere avanti e indietro e rileva modifiche nel database |
La classe ResultSet
offre inoltre diversi metodi per leggere i campi di un record. Questi metodi si differenziano in base al tipo di dato che si desidera recuperare. I principali metodi sono riportati
nella seguente tabella.
Metodo | Tipo di dato restituito |
---|---|
getInt(String colonna) |
int |
getString(String colonna) |
String |
getBoolean(String colonna) |
boolean |
getDouble(String colonna) |
double |
getDate(String colonna) |
java.sql.Date |
getTimestamp(String colonna) |
java.sql.Timestamp |
getBlob(String colonna) |
java.sql.Blob |
I campi possono essere selezionati in due modi:
-
Per indice: utilizzando l’indice della colonna, ad esempio
rs.getString(1)
per recuperare il primo campo (gli indici delle colonne partono da 1, non da 0); -
Per nome: indicando nome della colonna, ad esempio
rs.getString("nomeColonna")
.
Dopo aver creato il ResultSet
con CONCUR_UPDATABLE
, è possibile modificarne il contenuto:
Statement stmt = conn.createStatement(
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_UPDATABLE
);
ResultSet rs = stmt.executeQuery("SELECT id, nome FROM conti");
if (rs.next()) {
rs.updateString("nome", "Nuovo Nome");
rs.updateRow();
}
Metodo di modifica | Descrizione |
---|---|
updateString("colonna", valore) | Modifica il valore di una colonna |
updateInt("colonna", valore) | Modifica un valore intero |
updateRow() | Salva le modifiche |
deleteRow() | Cancella la riga corrente |
insertRow() | Inserisce una nuova riga |