Como ejecutar un procedimiento almacenado desde Java con JDBC

Llamar a un procedimiento almacenado usando JDBC es muy similar a ejecutar una PreparedStatement, sino te acuerdas muy bien como funcionaba pues verlo en un post anterior en el que hice un pequeño tutorial de como usar JDBC aquí.

Como en esa ocasión use MySQL, esta vez para cambiar voy a usar Oracle, aunque a efectos prácticos la única diferencia es el driver que hay que cargar.

El código SQL para generar la tabla que voy a usar en este ejemplo es el siguiente. También he añadido unas pocas filas para poder ver los resultados.


CREATE TABLE ALUMNOS(
    ID NUMBER(5)
        CONSTRAINT ALUMNO_PRIMARY_KEY PRIMARY KEY,
    NOMBRE VARCHAR2(15),
    PAIS VARCHAR2(20),
    FECHA_NACIMIENTO DATE,
    SEXO VARCHAR2(1)
        CONSTRAINT ALUMNO_CHK_SEXO CHECK(SEXO IN('M','H')),
    CURSO VARCHAR2(20)
);

INSERT INTO ALUMNOS VALUES(1,'Pepito Gonzalez','Rumania','5-Mayo-95','H','Bases de datos');
INSERT INTO ALUMNOS VALUES(2,'Laura Sanz','Mexico','14-Abril-86','M','Contabilidad');
INSERT INTO ALUMNOS VALUES(213,'Luis Perez','Argentina','18-May-78','H','Cocina');
INSERT INTO ALUMNOS VALUES(12321,'Susana Perez','España','29-Sep-90','M','Java');
INSERT INTO ALUMNOS VALUES(12322,'Fernando Torres','España','30-Julio-91','H','Java');
INSERT INTO ALUMNOS VALUES(123,'Miriam Ruiz','Cuba','29-Octubre-75','M','Contabilidad');
INSERT INTO ALUMNOS VALUES(129,'Cristian Lopez','España','4-Julio-67','H','Economia');
INSERT INTO ALUMNOS VALUES(6235,'Simone Simons','Holanda','17-Enero-85','M','Ballet');
INSERT INTO ALUMNOS VALUES(10012,'Mustafa Kemal','Turquia','19-Mayo-81','H','Ballet');

El procedimiento almacenado que usaré en el ejemplo es este donde nos devuelve el nombre, el sexo y el curso del alumno cuyo id le pasamos como primer parámetro.


CREATE OR REPLACE PROCEDURE ObtenerDatosAlumno(p_id alumnos.id%TYPE,
       p_nombre OUT alumnos.nombre%TYPE, p_sexo OUT alumnos.sexo%TYPE,
       p_curso OUT alumnos.curso%TYPE) AS
BEGIN
     SELECT nombre, 
            CASE sexo
                 WHEN 'H' THEN 'HOMBRE'
                 ELSE 'MUJER'
            END,
            curso
            INTO p_nombre, p_sexo, p_curso
            FROM alumnos WHERE id=p_id;
END;

Con esto ya tenemos nuestra tabla y nuestro procedimiento almacenado guardados en la base de datos con lo que ya podemos crear una aplicación java que haga uso de ellos.

Como decía en este ejemplo nos conectamos con JDBC a una base de datos Oracle, por lo que lo primero que tenemos que hacer es añadir el .jar de Oracle a nuestro proyecto.

Ahora ya esta todo listo y solo falta ver el código necesario para hacer la llamada al procedimiento almacenado. Para hacerlo lo más sencillo posible vamos a hacer una aplicación de consola con un bucle para leer los IDs de los alumnos por teclado.


package com.poi;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author Iván Salas
 * <http://programandoointentandolo.com>
 */

public class EjemploProcAlmacenado {

    /**
     * @param args the command line arguments
     */

    public static void main(String[] args) {
        BufferedReader entrada = new BufferedReader(new InputStreamReader(System.in));
        int id = -1;
        Connection cn = null;

        try {
            // Carga el driver de oracle
            DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
            
            // Conecta con la base de datos XE con el usuario system y la contraseña password
            cn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:XE", "system", "password");
            
            // Llamada al procedimiento almacenado
            CallableStatement cst = cn.prepareCall("{call ObtenerDatosAlumno (?,?,?,?)}");

            do {
                System.out.println("\nIntroduce el ID del alumno:");
                try {
                    id = Integer.parseInt(entrada.readLine());
                } catch (IOException ex) {
                    System.out.println("Error...");
                }
                
                // Parametro 1 del procedimiento almacenado
                cst.setInt(1, id);
                
                // Definimos los tipos de los parametros de salida del procedimiento almacenado
                cst.registerOutParameter(2, java.sql.Types.VARCHAR);
                cst.registerOutParameter(3, java.sql.Types.VARCHAR);
                cst.registerOutParameter(4, java.sql.Types.VARCHAR);
                
                // Ejecuta el procedimiento almacenado
                cst.execute();
                
                // Se obtienen la salida del procedimineto almacenado
                String nombre = cst.getString(2);
                String sexo = cst.getString(3);
                String curso = cst.getString(4);
                System.out.println("Nombre: " + nombre);
                System.out.println("Sexo: " + sexo);
                System.out.println("Curso: " + curso);
            } while (id > 0);

        } catch (SQLException ex) {
            System.out.println("Error: " + ex.getMessage());
        } finally {
            try {
                cn.close();
            } catch (SQLException ex) {
                System.out.println("Error: " + ex.getMessage());
            }
        }
    }
}

El código es bastante sencillo, pero vamos a verlo en detalle.

  • Las llamadas a los procedimientos almacenados al igual que las PreparedStatements y las consultas simples se hacen sobre la conexión, en este caso con el método prepareCall() que nos devuelve un CallableStatement.
  • La llamada al procedimiento almacenado además de ir entre comillas por ser un string tiene que ir también entre llaves y tiene el siguiente formato «{call nombre_procedimiento(?,?,…)}» teniendo tantos interrogaciones como parámetros tenga el procedimiento almacenado.
  • Los parámetros de entrada como con las PreparedStatements se definen con los métodos setXXX().
  • Hay que definir el tipo de los parámetros de salida con registerOutParameter() donde debemos indicar el tipo del que será ese dato en la base de datos y no en java.
  • El procedimiento se ejecuta cuando llamamos al método execute, y como es lógico en el momento en el que se ejecute tienen que estar definidos todos los parámetros tanto de entrada como de salida.
  • Finalmente, una vez ejecutado el procedimiento almacenado podemos obtener los valores que devuelve usando el método getXXX() adecuado para cada caso recodando que ahora estamos obteniendo los valores en java por lo que para obtener un varchar usaremos getString() y no getVarchar().

Así de sencillo, es llamar a un procedimiento almacenado con JDBC y como siempre podéis descargaros el código completo del ejemplo desde aquí.