CodeGym /Cursos /Módulo 5. Spring /Trabajo con repositorios: CrudRepository, JpaRepository

Trabajo con repositorios: CrudRepository, JpaRepository

Módulo 5. Spring
Nivel 5 , Lección 3
Disponible

¡Bienvenido a uno de los temas más útiles y populares de Spring Data: el trabajo con repositorios! En esta lección veremos qué son los repositorios, para qué sirven, en qué se diferencian CrudRepository y JpaRepository, cómo nos facilitan la vida y, por supuesto, cómo integrarlos en nuestra aplicación. Todo esto te será útil no solo en tus proyectos de estudio, sino también en el trabajo real, porque los repositorios son la base para interactuar con la base de datos en Spring.


¿Qué es un repositorio?

Un repositorio (Repository) en este contexto es la capa de la aplicación que se encarga de la interacción con la base de datos. Abstrae las operaciones básicas como crear, leer, actualizar y eliminar datos (las llamadas operaciones CRUD).

Antes, interactuar con la base de datos requería escribir consultas SQL a mano; Spring Data simplifica este proceso muchísimo. Ya no hace falta "ensuciarse las manos" con SQL (aunque nadie te lo prohíbe si quieres). En su lugar, los repositorios te permiten centrarte en la lógica de negocio, confiando las tareas rutinarias a Spring.


CrudRepository y JpaRepository: diferencias

Spring Data ofrece varios interfaces para trabajar con la base de datos, pero dos de ellos se usan con más frecuencia:

CrudRepository

CrudRepository — es la interfaz básica para ejecutar operaciones CRUD. Estos son sus métodos principales:


public interface CrudRepository<T, ID> {
<S extends T> S save(S entity);           // Guardar (o actualizar) la entidad
  Optional<T> findById(ID id);             // Buscar entidad por identificador
    boolean existsById(ID id);               // Comprobar si existe el registro
    Iterable<T> findAll();                   // Obtener todas las entidades
      void deleteById(ID id);                  // Eliminar por identificador
      void delete(T entity);                   // Eliminar la entidad pasada
}

Cuándo usarlo: si solo necesitas implementar las operaciones CRUD básicas.

JpaRepository

JpaRepository extiende CrudRepository y añade nuevas capacidades:

  • Paginación (dividir una lista grande en páginas, como en Google: 10 resultados por página)
  • Ordenación de datos
  • Métodos específicos de JPA como flush() para gestionar el estado de la base

Ejemplo de paginación:


// Obtener los primeros 20 usuarios, ordenados por nombre
Page<User> users = repository.findAll(PageRequest.of(0, 20, Sort.by("name")));

Ejemplos de métodos de JpaRepository:


public interface JpaRepository<T, ID> extends CrudRepository<T, ID> {
List<T> findAll(Sort sort);          // Ordenar resultados
  Page<T> findAll(Pageable pageable);  // Paginación de resultados
    void flush();                        // Guardar los cambios en la base de datos
    <S extends T> S saveAndFlush(S entity); // Guardar y hacer flush del contexto
}

Cuándo usarlo: si necesitas capacidades adicionales a nivel DAO, como ordenación o paginación.


Uso práctico de los repositorios

Ahora que la teoría está un poco más clara, ¡pasemos a la práctica! Trabajaremos con una aplicación que ya tiene la entidad User.

1. Entidad User

Nuestra clase User es una entidad muy simple que almacenará datos sobre el usuario:


import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String email;

    public User() {}

    public User(String name, String email) {
        this.name = name;
        this.email = email;
    }

    // Getters y 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;
    }
}

2. Creación del repositorio

Ahora vamos a crear la interfaz del repositorio. Spring Data hará todo lo demás por nosotros:


import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository<User, Long> {
    // Aquí puedes añadir métodos personalizados
}

¡Y eso es todo! El repositorio está listo. Ahora tenemos acceso a todas las operaciones CRUD que proporciona JpaRepository.


Realizar operaciones CRUD

Probemos usar nuestro repositorio para operaciones básicas.

Guardar usuario


@Service
public class UserService {
    private final UserRepository userRepository;

    @Autowired
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public User createUser(String name, String email) {
        User user = new User(name, email);
        return userRepository.save(user);
    }
}

Obtener usuario por ID


public Optional<User> getUserById(Long id) {
    return userRepository.findById(id);
}

Actualizar datos del usuario


public User updateUser(Long id, String name, String email) {
    User user = userRepository.findById(id).orElseThrow(() -> new RuntimeException("User not found"));
    user.setName(name);
    user.setEmail(email);
    return userRepository.save(user);
}

Eliminar usuario


public void deleteUser(Long id) {
    userRepository.deleteById(id);
}

Uso en un controlador REST Vamos a crear un controlador que use nuestro UserService:


@RestController
@RequestMapping("/users")
public class UserController {
    private final UserService userService;

    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }

    @PostMapping
    public ResponseEntity<User> createUser(@RequestBody User user) {
        User savedUser = userService.createUser(user.getName(), user.getEmail());
        return new ResponseEntity<>(savedUser, HttpStatus.CREATED);
    }

    @GetMapping("/{id}")
    public ResponseEntity<User> getUserById(@PathVariable Long id) {
        return userService.getUserById(id)
            .map(user -> new ResponseEntity<>(user, HttpStatus.OK))
            .orElse(new ResponseEntity<>(HttpStatus.NOT_FOUND));
    }

    @PutMapping("/{id}")
    public ResponseEntity<User> updateUser(@PathVariable Long id, @RequestBody User user) {
        User updatedUser = userService.updateUser(id, user.getName(), user.getEmail());
        return new ResponseEntity<>(updatedUser, HttpStatus.OK);
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
        userService.deleteUser(id);
        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
    }
}

Métodos personalizados en repositorios

A veces las operaciones estándar no son suficientes y necesitas escribir algo propio. Por ejemplo, buscar un usuario por nombre.

Implementación usando el nombre del método


List<User> findByName(String name);

Spring Data entiende "mágicamente" que quieres buscar usuarios por nombre, gracias a las convenciones de nombrado de métodos.

Implementación usando @Query


@Query("SELECT u FROM User u WHERE u.email = :email")
Optional<User> findByEmail(@Param("email") String email);

Paginación Si tienes muchos datos, puedes devolverlos en partes pequeñas:


Page<User> findAll(Pageable pageable);

Uso:


Pageable pageable = PageRequest.of(0, 10, Sort.by("name"));
Page<User> page = userRepository.findAll(pageable);

Posibles errores y cómo evitarlos

Aquí van algunos errores comunes que conviene recordar:

  • Error "No EntityManager with actual transaction": no olvides añadir la anotación @Transactional en los métodos de servicio que modifican datos.
  • Falta de constructor sin parámetros en la entidad: asegúrate de que las clases-entidad tengan un constructor público sin parámetros.
  • Problemas con la carga perezosa (LazyInitializationException): ten cuidado con lazy loading y trata de usar FetchType.LAZY solo donde sea necesario.

¿Para qué sirve todo esto?

Los repositorios son una forma estupenda de centrarte en la lógica de la aplicación sin perder tiempo en la rutina. Trabajar con repositorios es más rápido, más seguro y mucho más sencillo que escribir consultas SQL a mano.

En el trabajo real vas a usar repositorios constantemente, ya sea en una aplicación CRUD sencilla o en un sistema de microservicios complejo con decenas de bases de datos.

Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION