Capítulo 5: Introducción a los Repositories de Spring Data JPA
Introducción
Ahora que domina los fundamentos de Spring Boot (estructura, configuración, anotaciones), es momento de aprender a persistir datos en una base de datos. Spring Data JPA simplifica considerablemente esta tarea permitiéndole interactuar con la base de datos sin escribir código SQL.
Objetivos del Capítulo
Al final de este capítulo, usted será capaz de:
- Comprender el rol de un Repository de Spring Data JPA
- Utilizar los 4 métodos básicos para el CRUD
- Crear métodos de búsqueda simples con las convenciones de nombres
- Comprender cómo Spring genera automáticamente las consultas
¿Qué es un Repository?
Definición
Un Repository es una interfaz que permite interactuar con la base de datos sin escribir código SQL. Spring Data JPA genera automáticamente la implementación.
Analogía: Un repository es como un bibliotecario que gestiona sus libros (datos) por usted.
Arquitectura Completa
Responsabilidades:
| Capa | Rol | Ejemplo |
|---|---|---|
| Controller | Gestiona las solicitudes HTTP | @RestController |
| Service | Lógica de negocio | UserService |
| Repository | Acceso a los datos | UserRepository |
| Database | Almacenamiento | PostgreSQL, H2 |
Crear una Entidad Simple
Ejemplo: Entidad User
Antes de crear un repository, necesitamos una entidad - una clase Java que representa una tabla en la base de datos.
package com.example.demo.model;
import jakarta.persistence.*;
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
private int age;
// Constructeurs
public User() {
}
public User(String name, String email, int age) {
this.name = name;
this.email = email;
this.age = age;
}
// Getters et Setters
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
Anotaciones Esenciales
| Anotación | Rol | Ejemplo |
|---|---|---|
@Entity | Marca la clase como entidad JPA | Crea una tabla |
@Table | Especifica el nombre de la tabla | name = "users" |
@Id | Identifica la clave primaria | Campo id |
@GeneratedValue | Generación automática del ID | Auto-incremento |
Los 4 Métodos Básicos
Interfaz JpaRepository
Spring Data JPA proporciona la interfaz JpaRepository que contiene ya todos los métodos CRUD básicos.
package com.example.demo.repository;
import com.example.demo.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
// ¡No es necesario escribir código!
// Los métodos se heredan automáticamente
}
Explicación:
JpaRepository<User, Long>:User: Tipo de la entidadLong: Tipo del ID
Métodos Heredados Automáticamente
Aquí están los 4 métodos esenciales que puede usar de inmediato:
1. findById() - Buscar por ID
Firma:
Optional<User> findById(Long id)
Uso:
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public User getUserById(Long id) {
return userRepository.findById(id)
.orElseThrow(() -> new RuntimeException("User not found"));
}
}
SQL generado:
SELECT * FROM users WHERE id = ?
2. findAll() - Buscar todos
Firma:
List<User> findAll()
Uso:
public List<User> getAllUsers() {
return userRepository.findAll();
}
SQL generado:
SELECT * FROM users
3. save() - Crear o Actualizar
Firma:
User save(User user)
Uso (Creación):
public User createUser(User user) {
return userRepository.save(user);
}
SQL generado (si ID es null):
INSERT INTO users (name, email, age) VALUES (?, ?, ?)
SQL generado (si ID existe):
UPDATE users SET name = ?, email = ?, age = ? WHERE id = ?
4. deleteById() - Eliminar por ID
Firma:
void deleteById(Long id)
Uso:
public void deleteUser(Long id) {
userRepository.deleteById(id);
}
SQL generado:
DELETE FROM users WHERE id = ?
Tabla Resumen
| Método | Acción | Parámetro | Retorno | SQL |
|---|---|---|---|---|
findById(id) | Leer uno | ID | Optional<User> | SELECT ... WHERE id = ? |
findAll() | Leer todos | Ninguno | List<User> | SELECT * |
save(user) | Crear/Modificar | Entidad | User | INSERT o UPDATE |
deleteById(id) | Eliminar | ID | void | DELETE ... WHERE id = ? |
Convenciones de Nombres: Las 3 Bases
Principio General
Spring Data JPA puede generar automáticamente métodos de búsqueda basados en el nombre del método.
Formato:
findBy[Atributo]
1. findBy (Búsqueda Simple)
Sintaxis:
findBy + NombreAtributo
Ejemplos:
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
// Búsqueda por nombre
User findByName(String name);
// Búsqueda por email
User findByEmail(String email);
// Búsqueda por edad
List<User> findByAge(int age);
}
SQL generado:
| Método | SQL |
|---|---|
findByName("John") | SELECT * FROM users WHERE name = 'John' |
findByEmail("john@example.com") | SELECT * FROM users WHERE email = 'john@example.com' |
findByAge(25) | SELECT * FROM users WHERE age = 25 |
2. And (Combinar dos criterios)
Sintaxis:
findBy + Atributo1 + And + Atributo2
Ejemplos:
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
// Búsqueda por nombre Y edad
List<User> findByNameAndAge(String name, int age);
// Búsqueda por email Y edad
User findByEmailAndAge(String email, int age);
}
SQL generado:
| Método | SQL |
|---|---|
findByNameAndAge("John", 25) | SELECT * FROM users WHERE name = 'John' AND age = 25 |
findByEmailAndAge("john@example.com", 25) | SELECT * FROM users WHERE email = 'john@example.com' AND age = 25 |
3. Or (Criterio alternativo)
Sintaxis:
findBy + Atributo1 + Or + Atributo2
Ejemplos:
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
// Búsqueda por nombre O email
List<User> findByNameOrEmail(String name, String email);
// Búsqueda por edad 20 O 30
List<User> findByAgeOrAge(int age1, int age2);
}
SQL generado:
| Método | SQL |
|---|---|
findByNameOrEmail("John", "john@example.com") | SELECT * FROM users WHERE name = 'John' OR email = 'john@example.com' |
Ejemplo Completo: Arquitectura de 3 Capas
1. Entidad (Model)
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
private int age;
// Constructeurs, Getters, Setters
}
2. Repository
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
// Métodos heredados automáticamente:
// - findById(Long id)
// - findAll()
// - save(User user)
// - deleteById(Long id)
// Métodos personalizados:
User findByEmail(String email);
List<User> findByAge(int age);
List<User> findByNameAndAge(String name, int age);
}
3. Service
@Service
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
// CREATE
public User createUser(User user) {
return userRepository.save(user);
}
// READ - todos
public List<User> getAllUsers() {
return userRepository.findAll();
}
// READ - por ID
public User getUserById(Long id) {
return userRepository.findById(id)
.orElseThrow(() -> new RuntimeException("User not found with id: " + id));
}
// READ - por email
public User getUserByEmail(String email) {
return userRepository.findByEmail(email);
}
// READ - por edad
public List<User> getUsersByAge(int age) {
return userRepository.findByAge(age);
}
// UPDATE
public User updateUser(Long id, User userDetails) {
User user = getUserById(id);
user.setName(userDetails.getName());
user.setEmail(userDetails.getEmail());
user.setAge(userDetails.getAge());
return userRepository.save(user);
}
// DELETE
public void deleteUser(Long id) {
userRepository.deleteById(id);
}
}
4. Controller
@RestController
@RequestMapping("/api/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
// POST /api/users
@PostMapping
public User createUser(@RequestBody User user) {
return userService.createUser(user);
}
// GET /api/users
@GetMapping
public List<User> getAllUsers() {
return userService.getAllUsers();
}
// GET /api/users/123
@GetMapping("/{id}")
public User getUserById(@PathVariable Long id) {
return userService.getUserById(id);
}
// GET /api/users/email/john@example.com
@GetMapping("/email/{email}")
public User getUserByEmail(@PathVariable String email) {
return userService.getUserByEmail(email);
}
// GET /api/users/age/25
@GetMapping("/age/{age}")
public List<User> getUsersByAge(@PathVariable int age) {
return userService.getUsersByAge(age);
}
// PUT /api/users/123
@PutMapping("/{id}")
public User updateUser(@PathVariable Long id, @RequestBody User user) {
return userService.updateUser(id, user);
}
// DELETE /api/users/123
@DeleteMapping("/{id}")
public void deleteUser(@PathVariable Long id) {
userService.deleteUser(id);
}
}
Configuración de H2 para Pruebas
application.yml
spring:
datasource:
url: jdbc:h2:mem:testdb
driver-class-name: org.h2.Driver
username: sa
password:
h2:
console:
enabled: true
path: /h2-console
jpa:
hibernate:
ddl-auto: create-drop
show-sql: true
properties:
hibernate:
format_sql: true
Acceso a la consola H2:
http://localhost:8080/h2-console
Ejercicios Prácticos
Ejercicio 1: Repository Básico
Objetivo: Crear un repository simple
Instrucciones:
- Cree una entidad
Product:Long idString nameDouble priceint quantity
- Cree
ProductRepositoryque extiendaJpaRepository - Pruebe los 4 métodos básicos
Preguntas:
- ¿Qué método usar para crear un producto?
- ¿Cómo recuperar todos los productos?
- ¿Cómo eliminar un producto por su ID?
Ejercicio 2: Métodos de Búsqueda
Objetivo: Utilizar las convenciones de nombres
Instrucciones:
- En
ProductRepository, agregue:- Búsqueda por nombre
- Búsqueda por precio
- Búsqueda por nombre Y precio
- Cree un servicio
ProductService - Pruebe en un controlador
Preguntas:
- ¿Qué SQL se genera para
findByName? - ¿Cómo buscar los productos de un precio dado?
Ejercicio 3: Servicio Completo
Objetivo: Crear una arquitectura completa
Instrucciones:
- Cree una entidad
Student:Long idString firstNameString lastNameString emailint grade
- Cree
StudentRepositorycon:- Búsqueda por email
- Búsqueda por grade
- Búsqueda por firstName y lastName
- Cree
StudentServicecon todas las operaciones CRUD - Cree
StudentControllercon todos los endpoints
Preguntas de Repaso
-
¿Qué es un Repository en Spring Data JPA?
-
¿Cuáles son los 4 métodos heredados automáticamente de JpaRepository?
-
¿Cuál es la diferencia entre
save()para una creación y para una actualización? -
¿Cómo sabe Spring qué consulta SQL generar para
findByName? -
¿Cuál es la diferencia entre
AndyOren los nombres de los métodos? -
¿Por qué se utiliza
Optional<User>parafindById()? -
¿Qué sucede si se llama a
findById()con un ID que no existe? -
¿Se pueden combinar más de 2 criterios con
And? Dé un ejemplo.
Resumen del Capítulo
Puntos Clave
- Un Repository es una interfaz para acceder a los datos
JpaRepositoryproporciona 4 métodos básicos:findById,findAll,save,deleteById- Las convenciones de nombres permiten crear métodos automáticamente
- 3 palabras clave esenciales:
findBy,And,Or - Spring genera el SQL automáticamente basado en el nombre del método
- Arquitectura: Controller → Service → Repository → Database
Lo que Ahora Sabe
✅ Crear una entidad JPA
✅ Crear un repository Spring Data JPA
✅ Utilizar los 4 métodos CRUD básicos
✅ Crear métodos de búsqueda simples (findBy, And, Or)
✅ Construir una arquitectura de 3 capas completa
Lo que Aprenderá Más Adelante (Módulo 6)
🔜 Convenciones de nombres avanzadas (15+ palabras clave)
🔜 Joins entre entidades
🔜 Consultas JPQL personalizadas
🔜 Rendimiento y optimización
🔜 Transacciones
Buenas Prácticas
DO (A Hacer)
✅ Utilizar la inyección por constructor para los repositories
✅ Nombrar los métodos según las convenciones de Spring Data JPA
✅ Gestionar los casos donde findById() no encuentra nada con Optional
✅ Separar la lógica de negocio (Service) del acceso a datos (Repository)
DON'T (A Evitar)
❌ Colocar lógica de negocio en los repositories
❌ Llamar directamente al repository desde el controlador
❌ Ignorar el Optional retornado por findById()
❌ Crear nombres de métodos que no sigan las convenciones
Próximos Pasos
Poner en Práctica
Ahora que conoce los fundamentos de los repositories, ¡es momento de practicar!
Pase a los TPs del Módulo 2:
- TP2.2: Base de Datos H2 - Utilice H2 y los 4 métodos básicos
Para ir más lejos (siguientes módulos):
- Módulo 3: TP3.1: Arquitectura CRUD - Aplicación CRUD completa con validación
- Módulo 4: TP4.1: PostgreSQL - Migración a PostgreSQL en producción
Para Ir Más Lejos
¿Interesado en consultas más avanzadas? Consulte:
Módulo 6: Spring Data JPA Avanzado (próximamente)
- 15+ palabras clave de búsqueda
- Joins entre tablas
- JPQL y consultas personalizadas
- Optimización del rendimiento