Tutorial: Implementación en memoria del repositorio
1. Objetivos de aprendizaje
Sección titulada «1. Objetivos de aprendizaje»Al terminar este tutorial vas a ser capaz de:
- Usar un mapper de JSON a Java (
ObjectMapperde Jackson) para cargar datos reales en memoria, en vez de escribir cada objeto a mano. - Explicar por qué
TypeReferencees necesario para deserializar un tipo genérico comoList<ArtistaDTO>. - Adaptar un patrón de carga de datos de arranque a tu propio módulo.
- Anotar la clase que implementa tu interfaz de repositorio con
@Repository, para que Spring la registre como bean y se la pueda inyectar al controller.
Prerrequisitos: haber terminado el Tutorial: DTOs, repositorio y controller de tu API REST hasta el Paso 6 (tu controller ya compila), y tener tu propio Gist con datos reales de tu módulo.
2. Por qué cargar los datos así
Sección titulada «2. Por qué cargar los datos así»Hay dos formas de cargar tus datos reales en memoria. La primera es transcribir el JSON de tu Gist a objetos Java hardcodeados: por cada artista, crear el objeto, sus álbumes, las canciones de cada álbum. Es un trabajo mecánico, repetitivo, y fácil de hacer mal (un campo que no coincide, un álbum en el artista equivocado).
La segunda es usar un mapper: una librería que lee un JSON y construye
los objetos Java automáticamente, siempre que tengan la misma forma. Vas a
usar la segunda, con ObjectMapper de la librería Jackson.
A modo de recordatorio, así es como un mapper empareja cada propiedad del JSON con el atributo del mismo nombre (lo viste con más detalle en Implementación de un API REST con Spring):
JSON
{
"carnet": 12345,
"carne": 12345,
"nombre": "Camila Ruiz",
"cursosAprobados": [
{ "nombre": "Bases de Datos" },
{ "nombre": "Ingeniería de Software" }
]
}Java (DTOs)
@Data @NoArgsConstructor @AllArgsConstructor
public class EstudianteDTO {
private Long carnet;
private String nombre;
private List<CursoDTO> cursosAprobados;
}
public class CursoDTO {
private String nombre;
}Haz clic en una propiedad del JSON o en un atributo de la clase Java para ver con cuál se corresponde.
✅ Con estos nombres, el mapper asigna 12345 a EstudianteDTO.carnet.
⚠️ Con "carne" en el JSON, el mapper no encuentra una propiedad llamada carnet: el atributo queda en null, sin ningún error ni advertencia.
3. Preparar el proyecto
Sección titulada «3. Preparar el proyecto»3.1 Agrega la dependencia de Jackson
Sección titulada «3.1 Agrega la dependencia de Jackson»Con Spring Boot 4 (la versión que generó tu proyecto en el tutorial
anterior), Jackson quedó separado en su propio starter: spring-boot-starter-web
ya no lo trae por defecto. Agrega esta dependencia a tu pom.xml, dentro de
<dependencies>:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jackson</artifactId></dependency>3.2 Guarda tus datos como recurso
Sección titulada «3.2 Guarda tus datos como recurso»Guarda el JSON de tu Gist como un recurso del proyecto: crea el archivo
src/main/resources/datos.json y pega ahí el contenido completo de tu Gist,
tal cual.
4. Cómo funciona InMemoryArtistaRepository
Sección titulada «4. Cómo funciona InMemoryArtistaRepository»Con la dependencia y los datos listos, así se implementa el repositorio
(si tu proyecto quedó en Spring Boot 3.x, cambia los imports de Jackson a
com.fasterxml.jackson.*, ver sección 3.1):
package co.edu.uniandes.musical.artistas;
import tools.jackson.core.type.TypeReference;import tools.jackson.databind.ObjectMapper;import org.springframework.stereotype.Repository;
import java.io.InputStream;import java.util.List;
@Repositorypublic class InMemoryArtistaRepository implements ArtistaRepository {
private final List<ArtistaDTO> artistas;
public InMemoryArtistaRepository() { this.artistas = cargarDatos(); }
@Override public List<ArtistaDTO> findAll() { return artistas; }
private List<ArtistaDTO> cargarDatos() { ObjectMapper objectMapper = new ObjectMapper(); try (InputStream inputStream = getClass().getResourceAsStream("/datos.json")) { if (inputStream == null) { throw new IllegalStateException("No se encontró el archivo /datos.json en el classpath."); } return objectMapper.readValue(inputStream, new TypeReference<List<ArtistaDTO>>() {}); } catch (Exception e) { throw new IllegalStateException("Error al leer o deserializar el archivo /datos.json", e); } }}Fíjate en cada parte:
@Repository. Sin esta anotación, Spring no registra la clase como bean y tu controller falla al arrancar con un error de inyección de dependencias.private final List<ArtistaDTO> artistas, cargado una sola vez en el constructor. No necesitas unArrayListmutable: la lista se construye completa la primera vez y no vuelve a cambiar.getClass().getResourceAsStream("/datos.json"). Lee el archivo desde el classpath, no desde una ruta de disco. El/inicial significa “desde la raíz del classpath”: todo lo que pusiste ensrc/main/resources/queda ahí. Si el archivo no existe, este método devuelvenullen vez de lanzar una excepción, por eso elifexplícito.new TypeReference<List<ArtistaDTO>>() {}. Java borra la información de tipos genéricos en tiempo de ejecución (type erasure): en runtime,List<ArtistaDTO>es soloList.TypeReferencees el truco que usa Jackson para capturar el tipo genérico completo, así sabe que debe crear objetosArtistaDTO(y, dentro de cada uno,AlbumDTOyCancionDTO, siguiendo la forma anidada de tus DTOs) en vez de devolver mapas genéricos.catch (Exception e). Cualquier falla al leer o interpretar el JSON se convierte en un error de arranque explícito, en vez de dejar la lista vacía en silencio y que el error aparezca después, sin explicación, cuando el front no muestre datos.
5. Revisar antes de compilar
Sección titulada «5. Revisar antes de compilar»Antes de pegar esta clase en tu proyecto, revisa:
- ¿Cambiaste el paquete, el nombre de la clase y el DTO principal por los de tu propio módulo?
- ¿El nombre del archivo en
getResourceAsStreamcoincide con el que guardaste ensrc/main/resources/(sección 3.2)? - ¿Los imports de Jackson corresponden a tu versión de Spring Boot?
tools.jackson.*en Spring Boot 4 o superior,com.fasterxml.jackson.*en 3.x (sección 3.1). - ¿Agregaste la dependencia de Jackson al
pom.xmlsi tu proyecto es Spring Boot 4 o superior (sección 3.1)? - ¿Compila?
¿Dónde quedaste?
Sección titulada «¿Dónde quedaste?»Al final de este tutorial, ArtistaRepository ya tiene una implementación
real corriendo: tu backend responde GET /artistas con tus datos, cargados
en memoria a partir de tu Gist.
Vuelve al Paso 7 del tutorial anterior para conectar este backend con tu front en Angular.
Lo que viene después:
- Release 3: vas a agregar tu
primer
POST /artistas, todavía Controller → Repository directo, sin Service ni reglas de negocio. - Release 6: vas a reemplazar esta implementación en memoria por una real con JPA, sin tocar el controlador ni la interfaz.