Cuando trabajas con bases de datos relacionales, los procedimientos almacenados son una forma común de encapsular lógica de negocio a nivel de base de datos. En aplicaciones Java que usan JPA (Java Persistence API), llamar procedimientos almacenados ha evolucionado con los años. En este artículo veremos cómo llamar procedimientos almacenados usando versiones antiguas de JPA, las mejoras introducidas en JPA 2.1 / 2.2, y cómo aprovechar Spring Data JPA para un enfoque más declarativo.
¿Por qué usar procedimientos almacenados?
Los procedimientos almacenados son sentencias SQL precompiladas que residen en la base de datos. Ofrecen:
- Rendimiento: menor tráfico de red y mejores planes de ejecución.
- Seguridad: control de acceso a nivel de base de datos.
- Reutilización: lógica compartida entre aplicaciones y sistemas.
Llamar procedimientos almacenados con versiones antiguas de JPA (antes de JPA 2.1)
Antes de JPA 2.1, llamar procedimientos almacenados era algo más verboso y menos intuitivo. Los desarrolladores normalmente dependían del método createNativeQuery
del EntityManager
.
Ejemplo:
Supongamos que tenemos un procedimiento almacenado en MySQL:
CREATE PROCEDURE GET_EMPLOYEE_BY_ID(IN emp_id INT) BEGIN SELECT * FROM employee WHERE id = emp_id; END;
Puedes llamar este procedimiento en JPA de la siguiente forma:
Query query = entityManager.createNativeQuery("CALL GET_EMPLOYEE_BY_ID(?)", Employee.class); query.setParameter(1, 1001); List<Employee> result = query.getResultList();
Este enfoque funciona, pero carece de verificación de tipos y flexibilidad.
Mejoras en JPA 2.1 y versiones posteriores
Con JPA 2.1, el soporte para procedimientos almacenados pasó a formar parte de la especificación mediante la API StoredProcedureQuery
.
Usando StoredProcedureQuery
StoredProcedureQuery query = entityManager .createStoredProcedureQuery("GET_EMPLOYEE_BY_ID", Employee.class) .registerStoredProcedureParameter(1, Integer.class, ParameterMode.IN) .setParameter(1, 1001); List<Employee> result = query.getResultList();
Opcional: mapeo de parámetros de salida (OUT)
Para procedimientos con parámetros de salida, puedes registrarlos y obtenerlos así:
StoredProcedureQuery query = entityManager .createStoredProcedureQuery("GET_EMPLOYEE_SALARY") .registerStoredProcedureParameter("emp_id", Integer.class, ParameterMode.IN) .registerStoredProcedureParameter("salary", Double.class, ParameterMode.OUT) .setParameter("emp_id", 1001); Double salary = (Double) query.getOutputParameterValue("salary");
JPA 2.2 y 2.3 retienen en gran medida la misma API, con mejoras menores para integrarse con características de Java 8 como Optional
.
Usando procedimientos almacenados con Spring Data JPA
Spring Data JPA simplifica aún más las capas de acceso a datos al permitir que definas llamadas a procedimientos almacenados mediante interfaces de repositorio.
Ejemplo de configuración
Primero, define tu procedimiento almacenado:
CREATE PROCEDURE GET_TOTAL_EMPLOYEES(OUT total INT) BEGIN SELECT COUNT(*) INTO total FROM employee; END;
Luego, declara un método en tu repositorio y usa la anotación @Procedure
, indicando el atributo que coincide con el nombre del procedimiento almacenado:
public interface EmployeeRepository extends JpaRepository<Employee, Long> { @Procedure(name = "getTotalEmployees") Integer getTotalEmployees(); }
Si tu procedimiento está asociado con una entidad, anótalo:
@Entity @NamedStoredProcedureQuery( name = "getTotalEmployees", procedureName = "GET_TOTAL_EMPLOYEES", parameters = { @StoredProcedureParameter(mode = ParameterMode.OUT, name = "total", type = Integer.class) } ) public class Employee { @Id private Long id; private String name; }
Bonus: parámetros IN / OUT
Puedes usar @Procedure
con argumentos de entrada/salida:
@Procedure(procedureName = "GET_EMPLOYEE_SALARY") Double getEmployeeSalary(@Param("emp_id") Integer empId);
Spring se encarga del enlace de parámetros detrás de escena, haciendo que tu código sea más limpio y declarativo.
Resumen
Enfoque | Pros | Contras |
---|---|---|
JPA antiguo (SQL nativo) | Simple, funciona en todos lados | No es seguro con tipos, verboso |
JPA 2.1+ API | Seguro con tipos, estructurado | Un poco más de código repetitivo |
Spring Data JPA (declarativo) | Limpio, reutilizable | Flexibilidad limitada para procedimientos complejos |
Conclusión
Los procedimientos almacenados siguen siendo una herramienta valiosa en aplicaciones empresariales. Ya sea que estés atascado con una versión antigua de JPA o uses la pila moderna de Spring, tienes varias maneras de integrarlos de forma limpia y efectiva. Siempre equilibra legibilidad, reutilización y control al elegir tu enfoque.