Tecniche & Materiali
JDBC, Data Pump e ORA-12516: guida pratica
Un'applicazione Java si collega a Oracle tramite JDBC: il driver ojdbc fornisce l'implementazione dell'interfaccia java.sql.Connection, e la stringa di connessione indica host, porta, service name e credenziali. L'esportazione di una tabella con Data Pump si esegue con expdp da riga di comando, indicando directory, dumpfile e nome della tabella. L'errore ORA-12516 segnala che il listener non riesce ad allocare un nuovo processo server perché il numero di sessioni o processi ha raggiunto il limite configurato.
Come si collega Java a Oracle con JDBC?
JDBC (Java Database Connectivity) è l'API standard di Java per l'accesso ai database relazionali. Per Oracle serve il driver JDBC, distribuito da Oracle come file ojdbc11.jar (o ojdbc8.jar per Java 8). Il driver va inserito nel classpath dell'applicazione, oppure dichiarato come dipendenza Maven con coordinate com.oracle.database.jdbc:ojdbc11.
La connessione si apre con DriverManager.getConnection, passando una URL nel formato jdbc:oracle:thin:@host:porta/service. Il prefisso thin indica il driver puro Java, che non richiede client Oracle installato sulla macchina. Esempio minimo:
``java String url = "jdbc:oracle:thin:@db.example.com:1521/ORCLPDB1"; try (Connection conn = DriverManager.getConnection(url, "utente", "password"); PreparedStatement ps = conn.prepareStatement("SELECT id, nome FROM clienti WHERE attivo = ?")) { ps.setInt(1, 1); try (ResultSet rs = ps.executeQuery()) { while (rs.next()) { System.out.println(rs.getLong("id") + " " + rs.getString("nome")); } } } ``
Il blocco try-with-resources chiude automaticamente Connection, PreparedStatement e ResultSet, evitando sessioni lasciate aperte. In produzione conviene usare un pool di connessioni (HikariCP, UCP di Oracle) invece di aprire una connessione per ogni richiesta: il costo di handshake TCP e autenticazione è significativo. Per chi vuole approfondire il rapporto tra Java e database Oracle, esiste una guida Java e Oracle in spagnolo che raccoglie esempi verificati su JDBC, SQL e amministrazione.
Un dettaglio che genera errori frequenti: la URL jdbc:oracle:thin:@host:porta:SID (con i due punti prima del SID) è la sintassi per il vecchio identificatore di sistema, mentre il formato con la barra richiede il service name. Nei database multitenant il service name è quello del PDB, non del CDB.
Come si esporta una tabella con Data Pump?
Data Pump è l'utility di esportazione e importazione introdotta con Oracle 10g, che sostituisce exp e imp classici. Lavora lato server: i file di dump vengono scritti su una directory Oracle, non sul filesystem del client. Prima di esportare bisogna creare un oggetto DIRECTORY che punti a un percorso reale sul server, e concedere i privilegi di lettura e scrittura all'utente che esegue l'operazione.
``sql CREATE OR REPLACE DIRECTORY dp_dir AS '/u01/app/oracle/dump'; GRANT READ, WRITE ON DIRECTORY dp_dir TO modellista; ``
L'esportazione di una singola tabella si lancia da shell:
`` expdp modellista/password@ORCLPDB1 \ directory=dp_dir \ dumpfile=clienti_2024.dmp \ logfile=clienti_2024.log \ tables=CLIENTI \ compression=all ``
Il parametro tables accetta una lista separata da virgole. compression=all riduce la dimensione del dump comprimendo dati e metadati. Per esportare solo i dati senza DDL si aggiunge content=data_only; per includere indici e vincoli, content=all è il valore predefinito. Il file di log va sempre controllato: Data Pump non interrompe l'esecuzione al primo errore su una tabella, ma lo registra e prosegue.
L'importazione si esegue con impdp e gli stessi parametri di base, più eventuali rimappature:
`` impdp modellista/password@ORCLPDB1 \ directory=dp_dir \ dumpfile=clienti_2024.dmp \ remap_schema=modellista:test_user \ table_exists_action=replace ``
table_exists_action=replace elimina e ricrea la tabella di destinazione, utile in ambienti di test. In produzione è preferibile append o skip, valutando caso per caso. Data Pump richiede che la directory esista sul server e che l'utente abbia il privilegio EXP_FULL_DATABASE per esportare oggetti di altri schemi.
Che cosa fare con l'errore ORA-12516?
ORA-12516 si presenta con il messaggio "TNS:listener could not find available handler with matching protocol stack". In pratica il listener ha ricevuto la richiesta di connessione, ma non trova un processo server disponibile per gestirla. Le cause tipiche sono tre: il parametro processes del database è troppo basso, il parametro sessions è saturo, oppure il listener ha un numero massimo di connessioni configurato troppo basso.
La prima verifica è sul database:
``sql SHOW PARAMETER processes; SHOW PARAMETER sessions; SELECT resource_name, current_utilization, max_utilization, limit_value FROM v$resource_limit WHERE resource_name IN ('processes','sessions'); ``
Se current_utilization è vicino a limit_value, il problema è di configurazione. Aumentare processes richiede un riavvio del database; sessions si modifica a caldo ma è legato a processes (il valore predefinito è circa 1,1 volte processes più 5).
La seconda verifica è sul listener, nel file listener.ora. Il parametro PROCESSES del listener, se presente, limita il numero di connessioni che può accettare. Anche queuesize influisce sulla coda delle richieste in attesa. Dopo la modifica serve un lsnrctl reload.
Una causa meno ovvia è la presenza di sessioni inattive che non vengono chiuse. Le applicazioni Java che non restituiscono le connessioni al pool, o che usano connessioni senza timeout, accumulano sessioni fino a saturare il limite. Un controllo su v$session per stato e programma aiuta a identificare il client responsabile:
``sql SELECT machine, program, status, COUNT() FROM v$session GROUP BY machine, program, status ORDER BY COUNT() DESC; ``
Se il problema è un'applicazione che perde connessioni, la soluzione strutturale è configurare correttamente il pool: dimensione massima, timeout di inattività, validazione della connessione prima dell'uso. Aumentare processes senza correggere la perdita rimanda il problema di qualche giorno.
Perché il driver JDBC giusto cambia le prestazioni?
La versione del driver JDBC non è indifferente. Oracle pubblica driver allineati alle versioni di Java e del database, con correzioni di sicurezza e miglioramenti nel protocollo di rete. Un driver vecchio può non supportare il fetch size predefinito ottimale, oppure non gestire correttamente i tipi di dato introdotti nelle versioni recenti (JSON, XMLType, intervalli).
Il parametro defaultRowPrefetch controlla quante righe il driver recupera in un solo round trip verso il database. Il valore predefinito è 10; per query che restituiscono molte righe, alzarlo a 100 o 200 riduce drasticamente il numero di chiamate di rete. Si imposta nella URL di connessione:
`` jdbc:oracle:thin:@db.example.com:1521/ORCLPDB1?defaultRowPrefetch=200 ``
Attenzione alla memoria: ogni riga prefetch occupa spazio nell'heap della JVM. Valori troppo alti su result set molto larghi possono causare OutOfMemoryError. La misura va fatta sul caso concreto, non copiata da una guida generica.
Un altro parametro utile è oracle.jdbc.ReadTimeout, che interrompe una lettura bloccata dopo un numero di millisecondi. Senza timeout, una query lenta o un problema di rete lascia il thread Java in attesa indefinita, consumando una connessione del pool.
Quali controlli fare prima di andare in produzione?
Prima di distribuire un'applicazione Java che parla con Oracle, conviene verificare alcuni punti concreti. Il primo è la stringa di connessione: service name corretto, porta raggiungibile, eventuale wallet per la cifratura. Il secondo è il pool: dimensione massima coerente con il parametro sessions del database, non con il numero di utenti dell'applicazione. Il terzo è la gestione degli errori: distinguere tra errori transitori (ORA-12516, ORA-12541) e errori logici (ORA-00001 violazione di vincolo), perché i primi si possono ritentare, i secondi no.
Sul fronte Data Pump, la verifica riguarda lo spazio su disco della directory di dump e i privilegi dell'utente. Un'esportazione che fallisce a metà per disco pieno lascia file parziali che vanno rimossi prima di ritentare. Il log di Data Pump indica sempre il punto esatto dell'interruzione.
Infine, la documentazione ufficiale Oracle resta il riferimento per i parametri di inizializzazione e per i messaggi di errore: i valori predefiniti cambiano tra versioni, e una configurazione copiata da un ambiente 11g può non essere valida su 19c o 23c. La verifica sul campo, con query su v$resource_limit e v$session, è più affidabile di qualsiasi elenco precompilato.