Saltar al contenido principal

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:

CapaRolEjemplo
ControllerGestiona las solicitudes HTTP@RestController
ServiceLógica de negocioUserService
RepositoryAcceso a los datosUserRepository
DatabaseAlmacenamientoPostgreSQL, 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ónRolEjemplo
@EntityMarca la clase como entidad JPACrea una tabla
@TableEspecifica el nombre de la tablaname = "users"
@IdIdentifica la clave primariaCampo id
@GeneratedValueGeneración automática del IDAuto-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 entidad
    • Long: 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étodoAcciónParámetroRetornoSQL
findById(id)Leer unoIDOptional<User>SELECT ... WHERE id = ?
findAll()Leer todosNingunoList<User>SELECT *
save(user)Crear/ModificarEntidadUserINSERT o UPDATE
deleteById(id)EliminarIDvoidDELETE ... 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étodoSQL
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étodoSQL
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étodoSQL
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:

  1. Cree una entidad Product:
    • Long id
    • String name
    • Double price
    • int quantity
  2. Cree ProductRepository que extienda JpaRepository
  3. 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:

  1. En ProductRepository, agregue:
    • Búsqueda por nombre
    • Búsqueda por precio
    • Búsqueda por nombre Y precio
  2. Cree un servicio ProductService
  3. 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:

  1. Cree una entidad Student:
    • Long id
    • String firstName
    • String lastName
    • String email
    • int grade
  2. Cree StudentRepository con:
    • Búsqueda por email
    • Búsqueda por grade
    • Búsqueda por firstName y lastName
  3. Cree StudentService con todas las operaciones CRUD
  4. Cree StudentController con todos los endpoints

Preguntas de Repaso​

  1. ¿Qué es un Repository en Spring Data JPA?

  2. ¿Cuáles son los 4 métodos heredados automáticamente de JpaRepository?

  3. ¿Cuál es la diferencia entre save() para una creación y para una actualización?

  4. ¿Cómo sabe Spring qué consulta SQL generar para findByName?

  5. ¿Cuál es la diferencia entre And y Or en los nombres de los métodos?

  6. ¿Por qué se utiliza Optional<User> para findById()?

  7. ¿Qué sucede si se llama a findById() con un ID que no existe?

  8. ¿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
  • JpaRepository proporciona 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:

Para ir más lejos (siguientes módulos):

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

Volver al Módulo 2