Java JDBC CallableStatement
Appelez des procédures stockées depuis Java avec CallableStatement.
Un CallableStatement invoque une procédure stockée ou une fonction résidant dans la base de données. Il étend PreparedStatement, donc il dispose du même mécanisme de liaison par ? — plus la capacité d'enregistrer des paramètres OUT que la procédure vous renvoie en écriture. Utilisez-le lorsque la logique métier est implémentée dans la base de données plutôt qu'en Java.
Ce chapitre couvre la syntaxe d'échappement JDBC pour les appels, les trois directions de paramètres (IN, OUT, INOUT), pourquoi les paramètres OUT nécessitent un type enregistré, et l'ordre strict dans lequel vous devez appeler les méthodes.
La syntaxe d'échappement JDBC
Vous écrivez l'appel en utilisant la syntaxe portable avec accolades, que chaque pilote traduit dans le dialecte de son fournisseur :
// 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(?)}");Les accolades signifient que vous n'avez pas besoin de savoir si le fournisseur écrit CALL, EXEC, ou BEGIN ... END.
Paramètres IN, OUT et INOUT
Un paramètre de procédure a une direction :
- IN — vous le fournissez :
cs.setInt(1, customerId), exactement comme unPreparedStatement. - OUT — la procédure le remplit ; vous devez enregistrer son type d'abord, puis le lire après l'exécution.
- INOUT — les deux : définissez-le, puis enregistrez-le, puis lisez-le.
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 backLorsqu'une procédure produit une table complète au lieu d'une valeur unique, elle renvoie un ResultSet : appelez cs.executeQuery() (ou utilisez le boolean de cs.execute() avec cs.getResultSet()) et itérez-le exactement comme vous le feriez avec un ResultSet issu d'un Statement ordinaire.
Pourquoi registerOutParameter nécessite un type
JDBC doit savoir comment interpréter les octets que la base de données renvoie avant l'exécution de l'appel, vous nommez donc le type SQL avec une constante java.sql.Types. Si vous vous trompez de type, le getXxx ultérieur échouera ou effectuera une mauvaise conversion. L'ordre est fixe : enregistrer OUT → définir IN → exécuter → getXxx.
Un exemple concret : construire l'appel et enregistrer OUT
Ce programme construit les deux chaînes d'appel avec la syntaxe d'échappement et détaille l'enregistrement des paramètres OUT avec les codes java.sql.Types nécessaires — le protocole d'appel complet, exprimé sans base de données en direct.
Ce qu'il faut retenir de l'exécution :
- Les accolades
{call proc(?, ?)}constituent la syntaxe d'échappement portable. Vous écrivez la même chaîne quel que soit le fournisseur, et le pilote la réécrit — c'est ce qui rend les appels de procédures stockées indépendants de la base de données. - La forme
{? = call fn(?)}est destinée aux fonctions qui retournent une valeur : le?initial est l'emplacement de retour, enregistré comme paramètre OUT à l'index 1, avec les vrais arguments qui suivent. registerOutParameterprend une constantejava.sql.Types(INTEGERvaut 4,DECIMALvaut 3). C'est le même vocabulaire de types des chapitres précédents — JDBC le réutilise partout où il doit nommer un type SQL sans avoir de valeur Java sous la main.- Le type que vous enregistrez doit correspondre à ce que la procédure renvoie réellement ; sinon le
getInt/getBigDecimalultérieur lira les octets incorrectement. L'enregistrement est une promesse sur la forme du résultat. - L'ordre du protocole est strict et mérite d'être mémorisé : enregistrer les paramètres OUT, définir les paramètres IN,
execute(), puis lire les valeurs OUT avecgetXxx(index). L'exemple détaille cette séquence car ne pas la respecter est la cause habituelle des erreurs « parameter not registered ».