Java JDBC CallableStatement
Invoque stored procedures do Java com CallableStatement e parâmetros OUT.
Um CallableStatement invoca um stored procedure ou função que reside dentro do banco de dados. Ele estende o PreparedStatement, portanto possui a mesma vinculação de placeholder ? — além da capacidade de registrar parâmetros OUT que o procedimento escreve de volta para você. Use-o quando a lógica de negócios está implementada no banco de dados em vez de em Java.
Este capítulo aborda a sintaxe de escape JDBC para chamadas, as três direções de parâmetros (IN, OUT, INOUT), por que parâmetros OUT precisam de um tipo registrado e a ordem estrita em que você deve chamar os métodos.
A sintaxe de escape JDBC
Você escreve a chamada usando sintaxe de chaves portátil, que cada driver traduz para o dialeto do seu fornecedor:
// 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(?)}");As chaves significam que você não precisa saber se o fornecedor escreve CALL, EXEC ou BEGIN ... END.
Parâmetros IN, OUT e INOUT
Um parâmetro de procedimento tem uma direção:
- IN — você o fornece:
cs.setInt(1, customerId), exatamente como umPreparedStatement. - OUT — o procedimento o preenche; você deve registrar seu tipo primeiro, depois lê-lo após a execução.
- INOUT — ambos: defina-o, depois registre-o, depois leia-o.
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 um procedimento produz uma tabela completa em vez de um único valor, ele retorna um ResultSet: chame cs.executeQuery() (ou use o boolean de cs.execute() mais cs.getResultSet()) e itere-o exatamente como faria com um ResultSet de um Statement comum.
Por que registerOutParameter precisa de um tipo
O JDBC precisa saber como interpretar os bytes que o banco de dados envia de volta antes da chamada ser executada, por isso você nomeia o tipo SQL com uma constante java.sql.Types. Se o tipo estiver errado, o getXxx posterior falhará ou converterá incorretamente. A ordem é fixa: registrar OUT → definir IN → executar → getXxx.
Um exemplo prático: construindo a chamada e registrando OUT
Este programa constrói ambas as strings de chamada com sintaxe de escape e percorre o registro de parâmetros OUT com os códigos java.sql.Types necessários — o protocolo de chamada completo, expresso sem um banco de dados ativo.
O que extrair da execução:
- As chaves
{call proc(?, ?)}são a sintaxe de escape portátil. Você escreve a mesma string independentemente do fornecedor, e o driver a reescreve — isso é o que mantém as chamadas de stored procedure independentes do banco de dados. - A forma
{? = call fn(?)}é para funções que retornam um valor: o?inicial é o slot de retorno, registrado como parâmetro OUT no índice 1, com os argumentos reais seguindo-o. registerOutParameterrecebe uma constantejava.sql.Types(INTEGERé 4,DECIMALé 3). Esse é o mesmo vocabulário de tipos dos capítulos anteriores — o JDBC o reutiliza em todo lugar onde precisa nomear um tipo SQL sem um valor Java em mãos.- O tipo registrado deve corresponder ao que o procedimento realmente retorna; caso contrário, o
getInt/getBigDecimalposterior lerá os bytes incorretamente. Registrar é uma promessa sobre a forma do resultado. - A ordem do protocolo é estrita e vale a pena memorizar: registrar parâmetros OUT, definir parâmetros IN,
execute(), então ler valores OUT comgetXxx(index). O exemplo explicita essa sequência porque fazê-la fora de ordem é a causa usual de erros de "parameter not registered".