Repositoris a Spring Boot

Els repositoris permeten interactuar amb la base de dades de manera senzilla, evitant escriure SQL manualment i proporcionant una interfície clara per realitzar operacions comunes com consultar, guardar o eliminar dades.

Un repositori és una interfície que encapsula la lògica necessària per accedir a una base de dades. En el context de Spring Data JPA, els repositoris faciliten la gestió de les operacions CRUD (Create, Read, Update, Delete) sobre les entitats mitjançant un conjunt de mètodes predefinits.

Característiques dels repositoris:

  • Relació amb l’entitat: Cada repositori està associat a una entitat que representa una taula a la base de dades.
  • Generació automàtica: Spring Boot genera automàticament la implementació dels repositoris en temps d’execució.
  • Simplificació: Evita haver d’escriure manualment consultes SQL per a operacions bàsiques.
  • Convenció de noms: Permet definir mètodes personalitzats basant-se en noms específics que segueixen regles establertes.

Funcions dels repositoris

  1. Accedir a les dades: Permet obtenir, guardar, modificar i eliminar registres de la base de dades.
  2. Gestió d’operacions CRUD: Faciliten operacions com findById, save, delete, etc.
  3. Consultes personalitzades: Permeten definir mètodes per a consultes específiques utilitzant convencions de noms o anotacions.
  4. Abstracció: Ofereixen una capa que separa la lògica de negoci de l’accés a les dades.

1. Creació de Repositoris

Abans de crear un repositori, ens hem d’assegurar que tenim una entitat JPA definida. Una entitat és una classe que representa una taula a la base de dades:

  • Tenim una entitat definida amb l’anotació @Entity.
  • La classe d’entitat té una clau primària definida amb l’anotació @Id.

Exemple d’una entitat Ciutat:

import jakarta.persistence.Entity;
import jakarta.persistence.Id;

@Entity
public class Ciutat {

    @Id
    private Long id;
    private String nom;
    private int poblacio;
    private String pais;

    // Constructors, Getters i setters
}

Per crear un repositori, hem de definir una interfície que herete d’una de les interfícies de Spring Data JPA, com ara JpaRepository, CrudRepository o PagingAndSortingRepository.

Exemple de repositori per a l’entitat Ciutat:

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

public interface CiutatRepository extends JpaRepository<Ciutat, Long> {
    // Mètodes personalitzats es poden afegir ací
}
  • JpaRepository<Ciutat, Long>:
    • Ciutat: És l’entitat associada al repositori.
    • Long: És el tipus de la clau primària de l’entitat.

2. Mètodes predefinits

Quan heretem de JpaRepository, obtenim una sèrie de mètodes predefinits per gestionar dades:

Operacions bàsiques (CRUD):

  • save(T entity): Guarda o actualitza una entitat.
  • findById(ID id): Busca una entitat per la seva clau primària.
  • findAll(): Retorna totes les entitats.
  • deleteById(ID id): Elimina una entitat per la seva clau primària.
  • delete(T entity): Elimina una entitat específica.

Exemple d’ús en un servei:

@Service
public class CiutatService {

    @Autowired
    private CiutatRepository ciutatRepository;

    public List<Ciutat> obtenirTotesLesCiutats() {
        return ciutatRepository.findAll();
    }

    public Optional<Ciutat> obtenirCiutatPerId(Long id) {
        return ciutatRepository.findById(id);
    }

    public Ciutat guardarCiutat(Ciutat ciutat) {
        return ciutatRepository.save(ciutat);
    }

    public void eliminarCiutat(Long id) {
        ciutatRepository.deleteById(id);
    }
}

3. Consultes personalitzades mitjançant convencions de noms

La convenció de noms en Spring Data JPA permet que els mètodes dels repositoris es generen automàticament basant-se en els seus noms. Això elimina la necessitat d’escriure consultes SQL o JPQL manuals.

Funcionament:

  • Anàlisi del nom del mètode: Spring Data JPA interpreta el nom del mètode com una expressió lògica que indica quina consulta ha de generar.
  • Prefixos reconeguts:
    • findBy, getBy, readBy: Per cercar entitats.
    • countBy: Per comptar entitats.
    • deleteBy, removeBy: Per eliminar entitats.
    • existsBy: Per comprovar si existeixen entitats.

Exemples de mètodes basats en convencions de noms:

  1. Cerca per una propietat:
List<Ciutat> findByNom(String nom);

Genera la consulta:

SELECT * FROM ciutat WHERE nom = ?;
  1. Cerca per una propietat amb condició:
List<Ciutat> findByPoblacioGreaterThan(int poblacio);

Genera la consulta:

SELECT * FROM ciutat WHERE poblacio > ?;
  1. Combinació de condicions:
List<Ciutat> findByNomAndPais(String nom, String pais);

Genera la consulta:

SELECT * FROM ciutat WHERE nom = ? AND pais = ?;
  1. Comprovació d’existència:
boolean existsByNom(String nom);

Genera la consulta:

SELECT EXISTS (SELECT 1 FROM ciutat WHERE nom = ?);

  1. Comptar entitats:
long countByPais(String pais);

Genera la consulta:

SELECT COUNT(*) FROM ciutat WHERE pais = ?;
  1. Cerca amb ordre:
List<Ciutat> findByPaisOrderByPoblacioDesc(String pais);

Genera la consulta:

SELECT * FROM ciutat WHERE pais = ? ORDER BY poblacio DESC;
  1. Cerca per text parcial (LIKE en SQL):
List<Ciutat> findByNomContaining(String fragment);

Genera la consulta:

SELECT c.* FROM ciutat c WHERE c.nom LIKE %?%;


4. Consultes personalitzades amb @Query

  • Quan la convenció de noms no és suficient, pots definir consultes personalitzades amb l’anotació @Query.
  • @Query ens permet escriure consultes en JPQL (Java Persistence Query Language) o, si ho especifiquem, en SQL natiu.

Exemple:

import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

public interface CiutatRepository extends JpaRepository<Ciutat, Long> {

    @Query("SELECT c FROM Ciutat c WHERE c.poblacio > :poblacioMinima")
    List<Ciutat> trobarCiutatsAmbPoblacioMinima(@Param("poblacioMinima") int poblacioMinima);
}
  • :poblacioMinima éss un paràmetre de consulta que es representa amb : i un nom (poblacioMinima). El valor serà proporcionat com a argument quan cridem el mètode en el codi.
  • @Param("poblacioMinima") indica que el paràmetre de la funció trobarCiutatsAmbPoblacioMinima(int poblacioMinima) està associat amb :poblacioMinima en la consulta JPQL, el que ens permet passar valors dinàmics a la consulta.

5. Tipus de repositoris

Spring Data ofereix diversos tipus de repositoris segons les necessitats:

  1. CrudRepository:
    • Ofereix operacions bàsiques de CRUD.
    • Exemple: CrudRepository<T, ID>.
  2. JpaRepository:
    • Estén CrudRepository i afegeix funcionalitats avançades com paginació i ordenació.
    • Exemple: JpaRepository<T, ID>.
  3. PagingAndSortingRepository:
    • Ofereix suport per a paginació i ordenació.
    • Exemple: PagingAndSortingRepository<T, ID>.
  • Mètodes principals dels repositoris
Mètode Descripció Tipus de repositori
save(T entity) Guarda o actualitza una entitat. CrudRepository, JpaRepository
findById(ID id) Busca una entitat per la seva clau primària (ID). CrudRepository, JpaRepository
existsById(ID id) Comprova si existeix una entitat amb un ID específic. CrudRepository, JpaRepository
findAll() Retorna totes les entitats de la taula. CrudRepository, JpaRepository
findAllById(Iterable<ID> ids) Retorna totes les entitats que coincideixen amb els IDs especificats. CrudRepository, JpaRepository
deleteById(ID id) Elimina una entitat pel seu ID. CrudRepository, JpaRepository
delete(T entity) Elimina una entitat específica. CrudRepository, JpaRepository
deleteAll() Elimina totes les entitats. CrudRepository, JpaRepository
count() Retorna el nombre total d’entitats de la taula. CrudRepository, JpaRepository
findAll(Sort sort) Retorna totes les entitats ordenades segons els criteris especificats. PagingAndSortingRepository, JpaRepository
findAll(Pageable pageable) Retorna totes les entitats amb suport de paginació. PagingAndSortingRepository, JpaRepository
flush() Força l’execució immediata de les operacions pendents a la base de dades. JpaRepository
saveAndFlush(T entity) Guarda l’entitat i immediatament executa l’operació de base de dades. JpaRepository
deleteInBatch(Iterable<T> entities) Elimina un lot d’entitats en una sola operació. JpaRepository