Java JDBC CallableStatement
Richiama stored procedure dal database Java con CallableStatement e parametri OUT.
Un CallableStatement richiama una stored procedure o una funzione che risiede nel database. Estende PreparedStatement, quindi utilizza la stessa sintassi con segnaposto ? — con in più la possibilità di registrare parametri OUT che la procedura restituisce. Usalo quando la logica di business è implementata nel database anziché in Java.
Questo capitolo tratta la sintassi di escape JDBC per le chiamate, le tre direzioni dei parametri (IN, OUT, INOUT), il motivo per cui i parametri OUT richiedono un tipo registrato e il preciso ordine in cui bisogna invocare i metodi.
La sintassi di escape JDBC
La chiamata si scrive utilizzando la sintassi con parentesi graffe, che ogni driver traduce nel dialetto del proprio fornitore:
// procedure with two IN parameters
CallableStatement cs = conn.prepareCall("{call add_customer(?, ?)}");
// function returning a value, with one IN parameter
CallableStatement fn = conn.prepareCall("{? = call total_orders(?)}");Le parentesi graffe fanno sì che non sia necessario sapere se il fornitore usa CALL, EXEC o BEGIN ... END.
Parametri IN, OUT e INOUT
Un parametro di procedura ha una direzione:
- IN — lo fornisci tu:
cs.setInt(1, customerId), esattamente come con unPreparedStatement. - OUT — lo riempie la procedura; devi registrarne il tipo prima, poi leggerlo dopo l'esecuzione.
- INOUT — entrambi: impostalo, poi registralo, poi leggilo.
CallableStatement cs = conn.prepareCall("{call get_balance(?, ?)}");
cs.setInt(1, accountId); // IN
cs.registerOutParameter(2, java.sql.Types.DECIMAL); // OUT — declare its type
cs.execute();
BigDecimal balance = cs.getBigDecimal(2); // read it backQuando una procedura produce una tabella completa invece di un singolo valore, restituisce un ResultSet: chiama cs.executeQuery() (oppure usa il boolean di cs.execute() insieme a cs.getResultSet()) e iteralo esattamente come faresti con un ResultSet ottenuto da un normale Statement.
Perché registerOutParameter richiede un tipo
JDBC deve sapere come interpretare i byte restituiti dal database prima che la chiamata venga eseguita, quindi il tipo SQL viene specificato tramite una costante java.sql.Types. Se il tipo è errato, la successiva chiamata getXxx fallirà o effettuerà una conversione scorretta. L'ordine è fisso: registra OUT → imposta IN → esegui → getXxx.
Un esempio pratico: costruire la chiamata e registrare OUT
Questo programma costruisce entrambe le stringhe di chiamata con sintassi di escape e illustra la registrazione dei parametri OUT con i codici java.sql.Types necessari — il protocollo di chiamata completo, espresso senza un database reale.
Cosa osservare dall'esecuzione:
- Le parentesi graffe
{call proc(?, ?)}rappresentano la sintassi di escape portatile. Scrivi la stessa stringa indipendentemente dal fornitore, e il driver la riscrive — questo rende le chiamate alle stored procedure indipendenti dal database. - La forma
{? = call fn(?)}è per le funzioni che restituiscono un valore: il?iniziale è lo slot del valore restituito, registrato come parametro OUT all'indice 1, con i veri argomenti che seguono. registerOutParameteraccetta una costantejava.sql.Types(INTEGERvale 4,DECIMALvale 3). È lo stesso vocabolario di tipi dei capitoli precedenti — JDBC lo riutilizza ovunque debba nominare un tipo SQL senza avere un valore Java a disposizione.- Il tipo registrato deve corrispondere a ciò che la procedura restituisce effettivamente; altrimenti la successiva chiamata
getInt/getBigDecimalleggerà i byte in modo errato. La registrazione è una promessa sulla forma del risultato. - L'ordine del protocollo è rigido e vale la pena memorizzarlo: registra i parametri OUT, imposta i parametri IN, chiama
execute(), poi leggi i valori OUT congetXxx(index). L'esempio illustra questa sequenza perché eseguirla in ordine errato è la causa più comune degli errori "parameter not registered".