Cuando la lógica de negocio vive dentro de la base de datos, necesitas una forma limpia de invocarla desde tu aplicación. Spring Data JPA ofrece la anotación @Procedure, que permite declarar y ejecutar un stored procedure directamente desde un repositorio, sin escribir código JDBC manual. A través de un caso práctico —una promoción de pizza aleatoria con 20% de descuento— se muestra paso a paso cómo conectar un procedimiento almacenado con el resto de la arquitectura Spring.
¿Cómo funciona el stored procedure de la pizza aleatoria?
El procedimiento se llama take_random_pizza_order y simula una promoción: el cliente acepta recibir cualquier pizza al azar a cambio de un 20% de descuento [0:37]. Su estructura tiene tres partes fundamentales:
- Parámetros de entrada:
id_customer (identificación del cliente) y method (si es domicilio, para llevar o para consumir en el local) [1:02].
- Parámetro de salida (out):
order_taken, un booleano que indica si la orden se realizó con éxito [1:14].
- Variables internas: el ID de la pizza seleccionada, su precio original y el precio con descuento [1:24].
Dentro del procedimiento, la selección aleatoria se logra con la función RAND() de MySQL combinada con ORDER BY y LIMIT 1. Esto devuelve exactamente una pizza disponible de forma aleatoria [1:50]. Luego se calcula el precio con descuento restando el 20% al precio original.
El bloque transaccional utiliza START TRANSACTION para insertar un registro en pizza_order y otro en order_item. Si ocurre un error se ejecuta ROLLBACK y order_taken queda en false; si todo sale bien, se hace COMMIT y retorna true [2:40].
¿Qué es LAST_INSERT_ID y por qué es importante?
La función LAST_INSERT_ID() captura el identificador autogenerado de la fila recién insertada en pizza_order [2:28]. Ese valor se reutiliza para asociar correctamente el order_item con su orden correspondiente, manteniendo la integridad referencial.
¿Cómo se declara un stored procedure en Spring Data con @Procedure?
En el OrderRepository se crea un método que refleja la firma del procedimiento [3:17]:
java
@Procedure(value = "take_random_pizza_order", outputParameterName = "order_taken")
boolean saveRandomOrder(@Param("id_customer") String idCustomer,
@Param("method") String method);
- La anotación
@Procedure proviene de org.springframework.data.jpa.repository.query [3:55].
- El atributo
value recibe el nombre exacto del stored procedure en la base de datos.
outputParameterName indica el nombre del parámetro de salida, en este caso order_taken [4:15].
- Cada parámetro de entrada se anota con
@Param, y los nombres deben coincidir exactamente con los declarados en el procedimiento (id_customer, method) [4:30].
¿Cómo se conecta con el servicio y el controlador?
Se crea un DTO llamado RandomOrderDTO con la anotación @Data de Lombok, que contiene idCustomer y method [5:00]. En OrderService se expone un método que simplemente delega al repositorio:
java
@Transactional
public boolean saveRandomOrder(RandomOrderDTO dto) {
return orderRepository.saveRandomOrder(dto.getIdCustomer(), dto.getMethod());
}
En el controlador se mapea con @PostMapping("/random") y se recibe el DTO dentro del @RequestBody, retornando un ResponseEntity<Boolean> [5:40].
¿Por qué es obligatorio usar @Transactional con @Procedure?
Al ejecutar por primera vez sin @Transactional, Spring lanza un error 500 indicando explícitamente que los métodos anotados con @Procedure requieren un contexto transaccional [6:40]. Agregar @Transactional en el método del servicio resuelve el problema de inmediato.
Una vez corregido, la petición POST a /order/random con el JSON correspondiente retorna true [7:05]. Al consultar todas las órdenes se verifica que la última incluye la nota de la promoción del 20% y la pizza asignada aleatoriamente, por ejemplo, una pizza margarita [7:25].
- Verifica que el stored procedure exista en la base de datos antes de probar.
- Los nombres de los parámetros en
@Param deben ser idénticos a los del procedimiento.
- Siempre anota con
@Transactional el método que invoca @Procedure.
¿Has utilizado stored procedures en tus proyectos con Spring Data? Comparte tu experiencia y cuéntanos qué otros escenarios te han resultado útiles.