---
marp: true
theme: uncover
paginate: false
size: 16:9
backgroundColor: #f4f5f7
color: #1f2933
style: |
  section {
    font-size: 1.8em;
  }

  h1, h2, h3 {
    color: #0f172a;
  }

  /* Texto secundario */
  p, li {
    color: #1f2933;
  }

  /* Cajas destacadas */
  .box {
    border: 3px solid #2563eb;
    padding: 1em;
    border-radius: 12px;
    background-color: #e0e7ff;
    color: #1e293b;
  }

  .box-compact{
    font-size: 85% !important;
    line-height: 1.5;
  }

  .box-supercompact{
    font-size: 65% !important;
    line-height: 1.2;
  }

  .left{
    text-align: left;
  }

  .columns {
    display: flex;
    gap: 1em;
    justify-content: center;
    align-items: stretch;
  }


  .box.warning {
    border-color: #f97316;
    background-color: #ffedd5;
  }

  /* Alineación */
  .left {
    text-align: left;
  }

  /* Código */
  pre, code {
    background-color: #e5e7eb;
    color: #111827;
  }
  .w-30 { width: 30%; }
  .w-40 { width: 40%; }
  .w-50 { width: 50%; }
  .w-60 { width: 60%; }
  .w-70 { width: 70%; }
  .w-80 { width: 80%; }
  .center {
    margin-left: auto;
    margin-right: auto;
  }




---

# Spring Data:

### Persistencia en Sistemas Relacionales


<br/>
<br/>

##### Daniel García Costa  
###### 2026

---

## ¿Que vamos a ver?

- **¿Que es Spring Data?**
- **Modelo de dominio**
- **Consultas en Spring Data**
  - Métodos de abstracción
  - JPQL
  - SQL nativo
- **Rendimiento**
- **Optimizaciones**
- **Sistema de ejemplo**
- **Mucho código**

---

## ¿Qué es Spring Data?

Spring Data es el paquete del ecosistema Spring para **acceder a datos de forma consistente**.

- Define un modelo común de acceso a datos
- Orquesta todo lo relacionado con el acceso a datos
- Reduce código repetitivo
- Se integra con distintos motores

---

## Qué NO es Spring Data

Spring Data:

- ❌ No elimina el modelo relacional
- ❌ No es ni sustituye al ORM
- ❌ No optimiza consultas automáticamente
- ❌ No toma decisiones por nosotros

<br>
<div class="box w-90 center">
Facilita el acceso a datos, pero <strong>las decisiones siguen siendo nuestras</strong>.
</div>

---

## Capas de Spring Data 
<div class="box-compact center">

  **Aplicación**
  ↓  
  **Spring Data** — Abstracción de repositorios  
  <small>Abstracción para definir repositorios y consultas</small>
  ↓  
  **JPA** — API estándar de persistencia  
  <small>Especificación estándar (interfaces y anotaciones)</small>
  ↓  
  **Hibernate** — ORM (Object–Relational Mapping)  
  <small>Traducción y mapeo de objetos a SQL</small>
  ↓  
  **JDBC Driver** — Comunicación con la base de datos  
  <small>Ejecuta SQL contra la base de datos</small>
  ↓  
  **Base de datos**

</div>

---

## Idea clave

Spring Data facilita el acceso a datos pero **no elimina el modelo relacional**.

- Las queries siguen existiendo
- El SQL sigue ejecutándose
- Las decisiones siguen importando

---

## Modelo relacional

La base de datos trabaja con:

- Tablas
- Filas
- Claves primarias y ajenas
- Tipos simples
- Operaciones SQL

<br>
<div class="box w-60 center">
La lógica es <strong>estructural y declarativa</strong>.
</div>

---

## Modelo de dominio

La aplicación trabaja con:

- Objetos
- Relaciones entre entidades
- Comportamiento
- Identidad lógica
- Reglas de negocio

<br>
<div class="box w-50 center">
La lógica es <strong>orientada a objetos</strong>.
</div>

---

## El problema central

- El modelo relacional ≠ modelo de dominio
- Los objetos no son filas
- Las relaciones no son joins directos
- El rendimiento no es transparente

<br>
<div class="box w-50 center">
<strong>¡Necesitamos una capa intermedia!</strong>.
</div>


---

## ¿Qué consultamos en Spring Data?

En Spring Data **no consultamos tablas**.

Consultamos:

- Entidades de dominio
- Gestionadas por JPA
- Mapeadas a tablas relacionales

---

## Entidad como contrato

Una entidad define:

- Qué datos existen
- Cómo se relacionan
- Cómo se identifican
- Cómo se persisten

<br>
<div class="box w-70 center">
  <strong>La base de datos queda detrás de ese contrato</strong>.
</div>
<br>

*Usamos anotaciones para definir las propiedades y relaciones de nuestras entidades*

---

<div class="box-supercompact left">

| Categoría | Anotación | Para qué sirve |
|---------|----------|---------------|
| Entidad | `@Entity` | Marca la clase como entidad persistente |
|  | `@Table` | Define la tabla asociada |
| Identidad | `@Id` | Define la clave primaria |
|  | `@GeneratedValue` | Estrategia de generación del ID |
| Columnas | `@Column` | Configura nombre, nulabilidad, longitud |
|  | `@Basic` | Atributo persistente simple |
| Relaciones | `@ManyToOne` | Relación muchos‑a‑uno |
|  | `@OneToMany` | Relación uno‑a‑muchos |
|  | `@OneToOne` | Relación uno‑a‑uno |
|  | `@ManyToMany` | Relación muchos‑a‑muchos |
| Relaciones | `@JoinColumn` | Define la columna FK |
|  | `@JoinTable` | Tabla intermedia |
| Carga | `fetch = LAZY / EAGER` | Controla cuándo se cargan relaciones |
| Ciclo de vida | `@PrePersist` | Callback antes de insertar |
|  | `@PreUpdate` | Callback antes de actualizar |
| Temporal | `@Temporal` | Mapea fechas a tipos SQL |
| Herencia | `@Inheritance` | Estrategia de herencia |
| Control ORM | `@Transient` | Campo no persistente |

</div>

---

## Anotaciones de control (Spring Data / JPA)

<div class="left">

| Categoría | Anotación | Para qué sirve |
|---------|----------|---------------|
| Transacciones | `@Transactional` | Delimita una transacción |
|  | `@Modifying` | Indica que una query modifica datos (DML) |
| Ciclo de vida | `@PrePersist` | Antes de insertar |
|  | `@PostPersist` | Después de insertar |
|  | `@PreUpdate` | Antes de actualizar |
|  | `@PostUpdate` | Después de actualizar |
|  | `@PreRemove` | Antes de borrar |
|  | `@PostRemove` | Después de borrar |

</div>


---

## Inicialización del esquema (DDL)

<div class="left">

| Opción | Comportamiento |
|------|---------------|
| `none` | No hace nada con el esquema |
| `validate` | Verifica que el esquema exista |
| `update` | Sincroniza cambios automáticamente |
| `create` | Borra y crea el esquema |
| `create-drop` | Crea al arrancar, borra al parar |

</div>


Se configura en el application.properties
(`spring.jpa.hibernate.ddl-auto`)

---

## Impacto real del DDL automático
<br>
<br>

- Útil en desarrollo
- Peligroso en producción
- No sustituye migraciones
- Puede generar cambios costosos

<br>
<br>

**Decisión de arquitectura, no de código**

---

## ¿Dónde queda el SQL?

Aunque trabajemos con entidades:

- SQL sigue existiendo
- Hibernate lo genera o lo ejecuta
- La base de datos decide el coste (planificador del SGBD)

<br>
<div class="box w-80 center">
Spring Data <strong>te abstrae de las consultas</strong> pero <strong>no las elimina</strong>.
</div>

---

## Repository

Un Repository es:

- Una abstracción
- Orientada al dominio
- Tipada con entidades
- Independiente del motor SQL

<br>
<div class="box w-70 center">
Es la <strong>frontera</strong> entre la aplicación y la persistencia.
</div>

---

## Tres formas de consultar datos

En Spring Data podemos consultar usando:

1. Métodos de abstracción del Repository 
(<small>https://docs.spring.io/spring-data/jpa/reference/jpa/query-methods.html</small>)
2. JPQL (`@Query`)  
3. SQL nativo (`nativeQuery`)

<br>
<div class="columns">
  <div class="box warning w-40 center">

  **A mayor abstracción:**
    - Menos control
    - Más conveniencia
  </div>
  <div class="box warning w-40 center">

  **A menor abstracción:**
    - Más control
    - Más responsabilidad
  </div>
</div>

---

## Rendimiento

Cuando trabajamos con bases de datos:

- Todas las consultas tienen coste
- El volumen de datos importa
- La abstracción no es gratuita
<br>
<div class="box w-80 center">
El rendimiento depende de <strong>cómo accedemos a los datos</strong>
</div>

---

## ¿La abstracción es más lenta?

Pues... no siempre...

Depende de:
- La consulta
- El volumen de datos
- El uso de entidades o proyecciones
- El trabajo extra del ORM

<br>
<div class="box w-90 center">
El problema no es la abstracción, es lo <strong>qué se pide a la base de datos</strong>.
</div>

---

## Costes ocultos habituales

- Cargar entidades completas
- Inicializar relaciones
- Consultas innecesarias
- `N+1 SELECT`
- Transferencia de datos excesiva

<br>
<div class="box w-90 center">
Muchos problemas no vienen del SQL, sino del <strong>modelo de acceso</strong>.
</div>

---

### Ejemplo N+1 SELECT
```

SELECT t1.*, t2.*, ((t1.actual/t2.total)*100) DIV 1 AS progreso, 
              CASE WHEN t1.actual = t2.total AND t1.nbpar = t2.total AND t1.nbcomp = 1 THEN 'bg-success' 
                   WHEN actual > 0 AND t1.fact <> factorial(actual) AND t1.nbcomp = 1 THEN 'bg-success'
                   WHEN t1.nbpar = t2.total AND t1.nbcomp = 0 THEN 'bg-danger' 
                   WHEN actual > 0 AND t1.fact = factorial(actual) THEN 'bg-info'
                   WHEN actual > 0 AND t1.fact <> factorial(actual) AND t1.nbcomp = 0 THEN 'bg-warning'
                   WHEN t1.discard = 1 THEN 'bg-secondary'
                   ELSE 'text-black'
              END AS class,
              CASE 
                WHEN t1.actual = t2.total AND t1.nbpar < t2.total AND t1.nbcomp = 1 THEN '*' 
                ELSE ''
                END AS aux
        FROM 
        (SELECT al.usuario, al.id_alumno, al.observaciones AS obs, al.muerto AS discard, MAX(n_problema) AS actual, 
		CAST(SUM(CASE WHEN completa = 0 THEN 1 ELSE 0 END) AS SIGNED) AS nbpar, 
    CAST(SUM(CASE WHEN completa = 1 THEN 1 ELSE 0 END)AS SIGNED) AS nbcomp, 
        SUM(n_problema) AS fact
        FROM awpsolver.resultados_json rj
        LEFT JOIN awpsolver.alumnos al ON al.id_alumno = rj.id_alumno
        WHERE rj.id_grupo_experimento = :v
        GROUP BY al.usuario, al.id_alumno
        UNION
        SELECT al.usuario, al.id_alumno, al.observaciones AS obs, al.muerto AS discard, 0 AS actual,
        0 AS nbpar, 0 AS nbcomp, 0 AS self_fact 
        FROM awpsolver.alumnos al
        LEFT JOIN grupos_experimentos ge ON al.id_grupo = ge.id_grupo
        WHERE ge.id_grupo_experimento = :v AND al.id_alumno NOT IN(
            SELECT id_alumno FROM awpsolver.resultados_json WHERE id_grupo_experimento = :v)
        ORDER BY 2) AS t1, 
        (SELECT COUNT(*) AS total, factorial(COUNT(*)) AS fact FROM awpsolver.problemas WHERE id_experimento 
        IN(SELECT id_experimento FROM awpsolver.grupos_experimentos WHERE id_grupo_experimento = :v)) AS t2;
```

---

## JPQL vs SQL nativo

Ambos ejecutan SQL.

Diferencias:
- JPQL prioriza el modelo de dominio
- SQL nativo prioriza el modelo relacional
- El control es distinto

<br>
<div class="box w-90 center">
El rendimiento depende más de la query que del lenguaje.
</div>

---

## Algunas técnicas para reducir coste
<div class="box-compact">

  **Queries específicas**
  <small>Aprovecha el SGBD, pierde el miedo a escribir 100 lineas de SQL</small>

  **Proyecciones**
  <small>Traer solo lo necesario (tablas intermedias, vistas materizalidas, ...).</small>

  **Agregados**
  <small>(`COUNT`, `SUM`, `AVG`) sobre el SGBD y no en dominio</small>

  **Paginación real**
  <small>Traer solo lo necesario, no todo + filtro posterior</small>
<div>
<div class="columns">
  <div class="box warning w-50 center">

  - Menos columnas = Menos memoria
  - Menos trabajo del ORM
  - Menos transferencia de datos
  </div>
  <div class="box w-40 center">
      <strong>Son la primera herramienta antes de optimizar consultas.</strong>
  </div>
</div>

---

## Y todo eso... ¿como lo vemos?

Simulación de un juego de estrategia MMO (multijugador masivo en línea) multimundo. 
<br>
- **Servidor Shard (mundo)**
    - Aloja jugadores
    - Contiene la dinámica del juego
    - Reenvía los eventos al Master
<br>
- **Servidor Master (coordinador)**
    - Recibe eventos de los Shard, consulta estados, jugadores, rankings, etc.

---

## Arquitectura 

- **Servidor Master**
    - Monolítico (un único servicio)
    - División lógica en controladores -> servicios -> repositorios
    - Solo recibe y agrega información
    - GUI de consulta
<br>
- **Servidor Shard**
    - Dos servicios (Persistence + Core)
    - Gestiona los eventos de los jugadores
    - GUI para simular jugadores
    - Reenvío de eventos a Master

---

## ¿Por qué esta arquitectura?

- Separación clara de responsabilidades
- Escalado horizontal de shards
- Aislamiento del dominio de juego
- Centralización de analítica
- Escenario realista para estudiar el comportamiento de las capas de persistencia

---

<img src="diagrama_componentes.png" width="90%"/>

---


<img src="diagrama_servicio_master.png" width="70%"/>


---


<img src="esquema_fisico_bds.png" width="95%"/>


---

## Clona los siguientes proyectos

- **Master**
  - https://inmaculados.uv.es/dagarcos/PSIM-master
<br>
- **Shard(s)**
  - https://inmaculados.uv.es/dagarcos/PSIM-shard-core
  - https://inmaculados.uv.es/dagarcos/PSIM-shard-persistence

<br>

#### Y ahora... ¡a ver código!