W3docs

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 un PreparedStatement.
  • 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 back

Quando 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.

java— editable, runs on the server

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.
  • registerOutParameter accetta una costante java.sql.Types (INTEGER vale 4, DECIMAL vale 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/getBigDecimal leggerà 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 con getXxx(index). L'esempio illustra questa sequenza perché eseguirla in ordine errato è la causa più comune degli errori "parameter not registered".

Esercizio

Pratica
Prima di eseguire un CallableStatement con un parametro OUT, cosa devi fare per quel parametro?
Prima di eseguire un CallableStatement con un parametro OUT, cosa devi fare per quel parametro?
Was this page helpful?