Named Parameters y DTOs en JPQL

Clase 24 de 31Curso de Java: Backend con Spring Boot

Contenido del curso

JPA con Spring y Spring Data

Resumen

Construir consultas dinámicas y seguras en Spring Data JPA requiere dominar los Named Parameters dentro de JPQL. Esta técnica permite enviar valores a una sentencia de consulta mediante nombres personalizados, que luego se enlazan con los parámetros de un método en el repositorio. Además, al combinar esta estrategia con un DTO (Data Transfer Object), es posible obtener únicamente los campos necesarios sin depender de la entidad completa.

¿Qué son los Named Parameters en JPQL?

Los Named Parameters son marcadores con nombre que se colocan dentro de una sentencia JPQL para representar valores que serán inyectados en tiempo de ejecución [0:12]. En lugar de escribir valores fijos en la consulta, se utiliza la sintaxis :nombreParametro, y después se vincula cada parámetro con una variable del método mediante la anotación @Param.

Esta práctica aporta dos ventajas principales:

  • Legibilidad: cada parámetro tiene un nombre descriptivo que facilita entender la consulta.
  • Seguridad: se evita la inyección de valores directos, ya que Spring Data se encarga de sanitizar los datos.

¿Cómo crear un DTO para representar resultados específicos?

Antes de escribir la consulta, es necesario definir una clase DTO que contenga solo los campos que se desean obtener [0:38]. En el ejemplo trabajado se crea la clase UserDTO dentro de una carpeta llamada dto, con tres atributos:

  • id: identificador del usuario.
  • name: nombre del usuario.
  • birthdate: fecha de nacimiento, de tipo LocalDate.

La clase incluye su constructor, los métodos getter y setter, y la sobreescritura del método toString para facilitar la impresión en consola. Un DTO es simplemente una clase cuya responsabilidad es transferir datos entre capas de la aplicación, sin lógica de negocio [4:42].

¿Cómo se estructura la sentencia JPQL con Named Parameters?

Dentro de la interfaz UserRepository, se define un método que retorna un Optional<UserDTO> llamado getAllByBirthdateAndEmail [1:05]. Este método recibe dos parámetros: un LocalDate para la fecha y un String para el email.

La sentencia JPQL se construye con la anotación @Query y luce así:

java @Query("SELECT new com.example.dto.UserDTO(u.id, u.name, u.birthdate) " + "FROM User u " + "WHERE u.birthdate = :parametroFecha " + "AND u.email = :parametroEmail") Optional<UserDTO> getAllByBirthdateAndEmail( @Param("parametroFecha") LocalDate date, @Param("parametroEmail") String email );

Observa varios puntos clave:

  • Se utiliza new seguido de la ruta completa del paquete de UserDTO para instanciar el DTO directamente en la consulta [1:30].
  • El alias u representa la entidad User y permite acceder a sus campos.
  • Los Named Parameters :parametroFecha y :parametroEmail se enlazan con las variables del método gracias a @Param [2:50].

¿Cómo se invoca el método desde la aplicación?

En la clase FundamentosApplication se llama al repositorio pasando los valores concretos, por ejemplo la fecha y el correo de un usuario llamado Daniela [3:22]. Si no se encuentra el registro, se lanza una excepción de tipo RuntimeException con un mensaje descriptivo.

java userRepository.getAllByBirthdateAndEmail(LocalDate.of(2021, 1, 1), "daniela@mail.com") .orElseThrow(() -> new RuntimeException("No se encontró el usuario a partir del Named Parameter"));

El resultado se imprime con el logger a nivel info, mostrando el objeto DTO con los campos solicitados [3:55].

¿Por qué usar un DTO en lugar de la entidad completa?

Al ejecutar la aplicación, el resultado muestra únicamente el ID, el nombre y el birthdate del usuario [4:10]. Esto significa que no se retorna la entidad completa, sino solo los datos necesarios representados en la clase DTO. Esta separación entre entidad y objeto de transferencia es fundamental para mantener un código limpio, reducir el acoplamiento y optimizar el rendimiento de las consultas.

Si ya dominas los Named Parameters y los DTOs, el siguiente paso lógico es comprender cómo gestionar operaciones de escritura y control transaccional con la anotación @Transactional. ¿Qué otros patrones utilizas al trabajar con Spring Data JPA? Comparte tu experiencia.