Invocar a procedimientos usando Spring Boot Data JPA

En este tutorial vamos a ver cómo podemos invocar a procedimientos o métodos almacenados en ORACLE usando Spring Boot Data JPA

Índice de contenidos

1. Introducción

En este tutorial vamos a ver las distintas posibilidades que tenemos a la hora de invocar a procedimientos o métodos almacenados en ORACLE usando Spring Boot Data JPA.

A modo de introducción previa al tutorial os dejo otra forma de hacerlo con Spring JDBC como nos indicaba Daniel en su tutorial.

2. Entorno

El tutorial está escrito usando el siguiente entorno:

  • Hardware: Portátil MacBook Pro 15′ (2.5 GHz Intel Core i7, 16GB DDR3, 500GB Flash Storage)
  • Sistema Operativo: Mac OS Sierra 10.13.4
  • Entorno de desarrollo: IntelliJ IDEA 2018.1
  • JDK 1.8
  • Apache Maven 3.5.0

3. Dependencias y configuración

Lo primero que haremos será crear un proyecto Maven Spring Boot y añadiremos las siguientes dependencias en el fichero pom.xml:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>com.oracle</groupId>
        <artifactId>ojdbc8</artifactId>
        <version>12.2.0.1</version>
    </dependency>
</dependencies>

Tras añadir las dependencias habrá que configurar el datasource en el fichero application.yml:

spring:
    datasource:
        url: jdbc:oracle:thin:@//localhost:5000/dbname
        username: user
        password: pass
        driver-class-name: oracle.jdbc.OracleDriver

4. Usando @NamedStoredProcedureQuery

La primera posibilidad que nos da Spring es definir el acceso mediante anotaciones de Java de la siguiente manera:

@Entity
@NamedStoredProcedureQueries({
    @NamedStoredProcedureQuery(
        name = "java_procedure_name",
        procedureName = "SCHEMA_NAME.PACKAGE_NAME.METHOD_OR_PROCEDURE_NAME",
        parameters = {
          @StoredProcedureParameter(mode=ParameterMode.IN, name="inputParam1", type=String.class),
          @StoredProcedureParameter(mode=ParameterMode.IN, name="inputParam2", type=String.class),
          @StoredProcedureParameter(mode=ParameterMode.OUT, name="outputParam", type=String.class)
    })
})
public class TableName implements Serializable {
}

Tras definir el procedimiento podremos invocarlo usando CrudRepository:

public interface TableNameRepository extends CrudRepository<TableName, Long> {
    @Procedure(name = "java_procedure_name")
    String procedureName(@Param("inputParam1") String inputParam1, @Param("inputParam2") String inputParam2);
}

5. Usando createStoredProcedureQuery

La siguiente forma que tenemos para invocar a los procedimientos es usar el método createStoredProcedureQuery(String procedureName) que expone javax.persistence.EntityManager.
De esta forma podremos definir la invocación de los procedimientos de forma dinámica:

@Repository
public class ProcedureInvoker {

    private final EntityManager entityManager;

    @Autowired
    public ProcedureInvoker(final EntityManager entityManager) {
        this.entityManager = entityManager;
    }

    ...
        StoredProcedureQuery storedProcedureQuery = entityManager.createStoredProcedureQuery("SCHEMA_NAME.PACKAGE_NAME.METHOD_OR_PROCEDURE_NAME");
        
        // Registrar los parámetros de entrada y salida
        storedProcedureQuery.registerStoredProcedureParameter("INPUT_PROCEDURE_PARAMETER_NAME", String.class, ParameterMode.IN);
        storedProcedureQuery.registerStoredProcedureParameter("OUTPUT_PROCEDURE_PARAMETER_NAME1", String.class, ParameterMode.OUT);
        storedProcedureQuery.registerStoredProcedureParameter("OUTPUT_PROCEDURE_PARAMETER_NAME2", Long.class, ParameterMode.OUT);

        // Configuramos el valor de entrada
        storedProcedureQuery.setParameter("INPUT_PROCEDURE_PARAMETER_NAME", "value")

        // Realizamos la llamada al procedimiento
        storedProcedureQuery.execute();

        // Obtenemos los valores de salida
        final String outputValue1 = (String) query.getOutputParameterValue("OUTPUT_PROCEDURE_PARAMETER_NAME1");
        final Long outputValue2 = (Long) query.getOutputParameterValue("OUTPUT_PROCEDURE_PARAMETER_NAME2");
    ...
}

También podremos invocar a procedimientos que devuelven un SYS_REFCURSOR y almacenar el resultado en un objecto List de la siguiente manera:

    ...
    storedProcedureQuery.registerStoredProcedureParameter(2, Class.class, ParameterMode.REF_CURSOR)

    storedProcedureQuery.execute();

    // Obtenemos el resultado del cursos en una lista
    List<Object[]> results = storedProcedure.getResultList();

    // Recorremos la lista con map y devolvemos un List<BusinessObject>
    storedProcedureResponse.stream().map(result -> new BusinessObject(
        (String) result[0],
	    (Long) result[1]
    )).collect(Collectors.toList());

6. Conclusiones

Usar anotaciones hará que tengamos un código más limpio y orientado a JPA pero tiene dos puntos en contra:

  • Solo podremos devolver un único parámetro o un cursor.
  • A nivel conceptual, un procedimiento no pertenece a una tabla sino a la base de datos. Pero Spring nos obliga a anotar la clase @NamedStoredProcedureQuery con @Entity cuando puede que el procedimiento que va a ser invocado devuelva valores que no representan ninguna tabla dentro de nuestro modelo de datos.

Usando createStoredProcedureQuery tendremos muchas más flexibilidad a la hora de obtener todo tipo de valores pero también hará que el código se pueda descontrolar sino tenemos cuidado a la hora de usar este tipo de estructuras.

Un saludo.

Alejandro

aalmazan@autentia.com

Comentarios

2 respuestas

  1. Buenas,
    Personalmente sigo prefiriendo un autogenerador frente a la solución que indicas. El uso de la anotación es muy «cool» pero vas a encontrarte una cantidad de problemas en hacer el mapping de tipos desde java al storedProcedureQuery que dependiendo del número de procedimientos que debas consumir, puede ser tu muerte.

    Saludos.

  2. Muy agradecido por el post, me ayudo mucho y muy interesante las dos formas de consumir los sp por mi parte estoy usando ambas.

    Saludos crack!

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

He leído y acepto la política de privacidad

Información básica acerca de la protección de datos

  • Responsable: IZERTIS S.A.
  • Finalidad: Envío información de carácter administrativa, técnica, organizativa y/o comercial sobre los productos y servicios sobre los que se nos consulta.
  • Legitimación: Consentimiento del interesado
  • Destinatarios: Otras empresas del Grupo IZERTIS. Encargados del tratamiento.
  • Derechos: Acceso, rectificación, supresión, cancelación, limitación y portabilidad de los datos.
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad

Consultor tecnológico de desarrollo de proyectos informáticos. Puedes encontrarme en Autentia: Ofrecemos servicios de soporte a desarrollo, factoría y formación. Somos expertos en Java/Java EE

¿Quieres publicar en Adictos al trabajo?

Te puede interesar

02/03/2026

José Antonio Sánchez Segovia

Zephyr es un RTOS open source respaldado por la Linux Foundation que permite desarrollar dispositivos embebidos conectados, eficientes y escalables, facilitando el paso de prototipo a producto final con una arquitectura mantenible.

23/02/2026

Enrique Casado Díez

LoRa y LoRaWAN son tecnologías clave en el ecosistema IoT cuando se requiere largo alcance y bajo consumo energético. En este artículo analizamos su funcionamiento, Spreading Factor, link budget, arquitectura de red, frecuencias y clases de dispositivos, con un caso práctico real.

19/02/2026

Juan José Díaz Antuña

Copilot Chat es la forma más sencilla y segura de empezar a usar IA en Microsoft 365. En este artículo vemos cómo funciona, cómo activarlo y en qué se diferencia de Microsoft 365 Copilot, Copilot Studio y los Agentes Inteligentes, con ejemplos prácticos y una comparativa clara.