W3docs

Connessione JDBC in Java

Apri e gestisci le connessioni al database in Java con l'interfaccia Connection — apertura, chiusura e configurazione.

Una Connection è una sessione attiva con il database. È l'oggetto restituito da DriverManager (o da un DataSource) ed è la factory per tutto il resto — statement, transazioni, savepoint, metadati. Una connessione è anche una risorsa scarsa e costosa: ogni connessione aperta occupa un socket e una sessione lato server, quindi la regola fondamentale è aprire tardi, chiudere subito.

Questo capitolo spiega come costruire un URL di connessione, i tre modi per aprire una Connection, perché il try-with-resources è imprescindibile, le impostazioni di sessione configurabili e gli errori che si incontrano in caso di configurazione errata. Si presuppone che il driver sia già stato caricato — consulta JDBC Drivers e JDBC Introduction per il quadro generale.

L'URL di connessione

Tutto ciò di cui DriverManager ha bisogno per trovare e raggiungere un database è codificato nell'URL:

jdbc:<subprotocol>://<host>:<port>/<database>?<key=value&...>

Ad esempio jdbc:postgresql://db.internal:5432/shop?ssl=true. Il prefisso jdbc: è obbligatorio; il sottoprotocollo seleziona il driver; il resto è specifico del vendor ma segue la convenzione host, porta, database e una query string di opzioni di configurazione. Alcuni URL reali da riconoscere:

DatabaseURL di esempio
PostgreSQLjdbc:postgresql://localhost:5432/shop
MySQLjdbc:mysql://localhost:3306/shop?useSSL=true
H2 (in-memory)jdbc:h2:mem:testdb
SQLite (file)jdbc:sqlite:/data/shop.db

Il sottoprotocollo (postgresql, mysql, h2, sqlite) è il meccanismo con cui DriverManager decide quale driver registrato deve gestire l'URL, quindi specificarlo correttamente indica a JDBC quale database si intende usare.

Tre modi per aprirla

// 1. URL with credentials as arguments
Connection a = DriverManager.getConnection(url, "app", "secret");

// 2. URL with a Properties bag (user, password, plus driver-specific keys)
Properties props = new Properties();
props.setProperty("user", "app");
props.setProperty("password", "secret");
props.setProperty("connectTimeout", "10");
Connection b = DriverManager.getConnection(url, props);

// 3. From a pooled DataSource (preferred for applications)
Connection c = dataSource.getConnection();

DriverManager vs. DataSource

DriverManager.getConnection(...) apre una connessione fisica nuova ogni volta, poi la smonta quando viene chiusa. Quella procedura di handshake — lookup DNS, TCP, TLS, autenticazione — costa decine di millisecondi, il che va bene per uno script ma è disastroso per un server che gestisce molte richieste.

Un DataSource supportato da un pool di connessioni (HikariCP, Apache DBCP o quello fornito dal server applicativo) mantiene aperto un insieme di connessioni fisiche e le distribuisce su richiesta. Chiamare getConnection() ne prende in prestito una; chiamare close() la restituisce al pool invece di chiuderla davvero. Per qualsiasi applicazione con esecuzione prolungata, preferisci un DataSource con pool; usa DriverManager solo in piccoli strumenti, test ed esempi.

Chiudi sempre — usa il try-with-resources

Connection, Statement e ResultSet implementano tutti AutoCloseable. Dichiararli nell'intestazione di un try-with-resources garantisce che vengano chiusi in ordine inverso anche se viene lanciata un'eccezione — è la singola abitudine più importante in JDBC:

try (Connection conn = DriverManager.getConnection(url, "app", "secret")) {
  // use conn...
} // conn.close() runs here automatically, even on exception

Far trapelare connessioni (dimenticare di chiuderle) esaurisce il pool e alla fine blocca l'intera applicazione — un classico blackout in produzione. Si noti che aprire una Connection lancia una SQLException checked, quindi la chiamata risiede sempre all'interno di un try (o di un metodo che dichiara throws SQLException).

Una volta ottenuta una connessione, la si usa per creare Statements e PreparedStatements — e questi devono stare all'interno della stessa intestazione try-with-resources in modo da chiudersi prima della connessione:

try (Connection conn = dataSource.getConnection();
     PreparedStatement ps = conn.prepareStatement("SELECT name FROM users WHERE id = ?")) {
  ps.setInt(1, 42);
  // ...read the ResultSet...
} // ps closes first, then conn — reverse declaration order

Configurare una connessione

Una volta aperta, una connessione porta con sé impostazioni a livello di sessione:

MetodoCosa fa
setAutoCommit(false)Avvia una transazione manuale; poi chiami commit() o rollback() tu stesso. Vedi JDBC Transactions.
setTransactionIsolation(...)Sceglie un livello di isolamento (es. TRANSACTION_READ_COMMITTED).
setReadOnly(true)Un suggerimento che la connessione non esegue scritture; alcuni driver la ottimizzano di conseguenza.
setSchema(...) / setCatalog(...)Seleziona il namespace contro cui vengono eseguite le query.
isValid(timeout)Restituisce true se la connessione è ancora attiva; i pool la usano per scartare quelle non valide.

Queste impostazioni sono per sessione, quindi si azzerano quando una connessione reale viene chiusa — ma con un pool, una connessione presa in prestito potrebbe portare ancora le impostazioni lasciate da un utente precedente. È per questo che i pool ripristinano autoCommit e l'isolamento al momento della restituzione, e perché dovresti impostare ciò di cui hai bisogno invece di assumere i valori predefiniti.

Errori di connessione comuni

Quando una connessione fallisce si ottiene quasi sempre una SQLException; il messaggio indica quale livello si è rotto:

  • No suitable driver found for ... — la classe del driver non è mai stata caricata, oppure il sottoprotocollo dell'URL è scritto male e nessun driver registrato lo riconosce. Correggi la dipendenza o l'URL (vedi JDBC Drivers).
  • Connection refused — niente è in ascolto su quell'host/porta: il database è inattivo, oppure host/porta nell'URL sono errati.
  • Authentication / password authentication faileduser o password errati, oppure l'utente non ha i diritti su quel database.
  • Connection timeout — l'host non è raggiungibile (firewall, rete errata). Imposta connectTimeout affinché la chiamata fallisca rapidamente invece di bloccarsi.

Un esempio pratico: l'anatomia di un URL di connessione

Questo programma scompone un URL JDBC realistico nei suoi componenti e costruisce il bag Properties da passare insieme ad esso — i due input di cui getConnection ha bisogno — senza richiedere un database attivo.

java— editable, runs on the server

Cosa trarre dall'esecuzione:

  • L'URL non è opaco — è un dato strutturato. getConnection analizza esattamente questi elementi: il sottoprotocollo sceglie il driver, e host/porta/database dicono a quel driver dove connettersi. Leggere un URL ad alta voce è il modo più rapido per individuare errori di "database sbagliato".
  • La query string (?ssl=true&applicationName=reports) trasporta opzioni specifiche del driver. Le stesse impostazioni possono viaggiare nell'URL oppure nel bag Properties — entrambi raggiungono il driver, e li si mescola a piacere.
  • Le credenziali appartengono a Properties (o alla configurazione del DataSource), non hardcodate nella stringa URL che finisce nei log. L'esempio maschera la password nell'output proprio per questo motivo — non registrare mai le credenziali nei log.
  • connectTimeout è una vera proprietà del driver PostgreSQL. La configurazione risiede in queste coppie chiave/valore, motivo per cui raramente si fa subclassing: è la configurazione, non il codice, a modellare una connessione.
  • Questo è stato eseguito senza database perché costruire gli input per getConnection è puro lavoro su stringhe. La parte costosa — il socket e la sessione server — avviene solo alla chiamata di getConnection, motivo per cui la si rinvia e la si chiude rapidamente.

Pratica

Pratica
Perché una Connection JDBC dovrebbe quasi sempre essere acquisita all'interno di un'istruzione try-with-resources?
Perché una Connection JDBC dovrebbe quasi sempre essere acquisita all'interno di un'istruzione try-with-resources?
Was this page helpful?