Contenido del curso

Spring Data Repositories

@Modifying para updates con @Query en JPA

Resumen

Modificar datos en una base de datos con Spring Data JPA va más allá de un simple save(). Cuando necesitas ejecutar un update, insert o delete desde una consulta personalizada, la anotación @Modifying se vuelve indispensable. Aquí aprenderás a combinarla con @Query para actualizar el precio de una pizza usando SQL nativo y Spring Expression Language.

¿Cómo crear un DTO para transferir datos en Spring Boot?

Antes de tocar el repositorio, necesitas un objeto que transporte la información entre capas. Un Data Transfer Object (DTO) cumple justo esa función: encapsular los datos que viajan desde el cliente hasta tu servicio.

Dentro del paquete service.dto creas la clase UpdatePizzaPriceDTO y la anotas con @Data de Lombok para generar automáticamente getters, setters y constructores [01:00]. Los atributos son simples:

  • private Integer pizzaId para identificar la pizza.
  • private double newPrice para el precio actualizado.

¿Qué es un DTO en Spring Boot? Es una clase que transporta datos entre capas sin lógica de negocio. Se usa para recibir o enviar información estructurada, como el ID de una pizza y su nuevo precio.

¿Cómo escribir un UPDATE con @Query y @Modifying?

En PizzaRepository defines un método updatePrice que no retorna nada. Lo anotas con @Query indicando que es una consulta nativa y construyes el SQL paso a paso [02:10]:

java @Modifying @Query(value = "UPDATE pizza " + "SET price = :#{#newPizzaPrice.newPrice} " + "WHERE id_pizza = :#{#newPizzaPrice.pizzaId}", nativeQuery = true) void updatePrice(@Param("newPizzaPrice") UpdatePizzaPriceDTO newPizzaPrice);

Fíjate en el detalle: cada línea termina con un espacio antes de las comillas para que Java concatene el SQL sin pegar palabras. Es un error silencioso muy común.

¿Qué es Spring Expression Language y cómo se usa en @Query?

En lugar de pasar dos parámetros sueltos, puedes recibir el DTO completo y extraer sus atributos dentro de la consulta. Para eso usas SpEL con la sintaxis :#{#nombreParametro.atributo} [03:30].

Esto te permite:

  • Reducir la firma del método a un solo parámetro.
  • Acceder a cualquier campo del objeto desde el SQL.
  • Mantener el código más limpio cuando trabajas con objetos compuestos.

No es obligatorio. Puedes seguir usando @Param tradicional con parámetros individuales, pero conocer SpEL te da flexibilidad cuando manejas objetos con varios atributos.

¿Por qué necesitas @Modifying y @Transactional juntas?

Aquí viene la parte que suele tropezar a quienes empiezan. Si solo pones @Query con un update, Spring lanza una excepción al ejecutarlo. La razón es simple: un @Query sin @Modifying solo puede hacer selects.

¿Cuándo usar @Modifying en Spring Data JPA? Siempre que tu @Query ejecute insert, update o delete. Sin esta anotación, Spring asume que la consulta es de lectura y rechaza la operación.

Pero con @Modifying no basta. Al ejecutar la primera petición aparece otro error 500 que pide envolver la operación en una transacción [07:15]. La solución es anotar el método del servicio con @Transactional de org.springframework.transaction.annotation:

java @Transactional public void updatePrice(UpdatePizzaPriceDTO dto) { pizzaRepository.updatePrice(dto); }

Cuidado con el import. Existen varias anotaciones @Transactional en el ecosistema Java y debes elegir la de Spring Framework.

¿Cómo exponer el endpoint de actualización en el controlador?

En PizzaController agregas un método PUT que escucha en /price. Antes de actualizar, validas que la pizza exista usando el método exists que ya construiste:

java @PutMapping("/price") public ResponseEntity<Void> updatePrice(@RequestBody UpdatePizzaPriceDTO dto) { if (this.pizzaService.exists(dto.getPizzaId())) { this.pizzaService.updatePrice(dto); return ResponseEntity.ok().build(); } return ResponseEntity.badRequest().build(); }

La lógica es directa: si la pizza existe, actualiza y responde 200 OK. Si no, devuelve 400 Bad Request.

¿Cómo probar el endpoint con Postman?

Para verificar que todo funcione, envías una petición PUT al endpoint /api/pizzas/price con un cuerpo JSON [09:20]:

{ "pizzaId": 7, "newPrice": 19.99 }

La pizza con ID 7, llamada Mother Heart, pasa de costar 19.5 a 19.99. Si vuelves a consultar /api/pizzas/7, el precio refleja el cambio. En la consola verás dos consultas ejecutándose en orden: primero el count del exists y luego el update que escribiste con @Modifying.

Con esto ya puedes escribir cualquier operación de modificación dentro de un @Query. ¿Has intentado combinar SpEL con consultas más complejas? Cuéntame en los comentarios cómo lo resolverías.