Vai al contenuto

Step 03 — JPA + Flyway + PostgreSQL

Obiettivo: persistere progetti e task in PostgreSQL con schema gestito da Flyway.

Cosa costruiamo

  • Entità JPA Project e Task con relazione 1:N.
  • Repository Spring Data.
  • Prima migration Flyway V1__init.sql.

Migration

src/main/resources/db/migration/V1__init.sql
create table projects (
    id           uuid           primary key,
    name         varchar(120)   not null,
    description  varchar(2000),
    status       varchar(20)    not null,
    created_at   timestamptz    not null default now(),
    updated_at   timestamptz    not null default now(),
    version      bigint         not null default 0,
    constraint chk_project_status check (status in ('ACTIVE', 'ARCHIVED'))
);

create table tasks (
    id           uuid           primary key,
    project_id   uuid           not null references projects (id) on delete cascade,
    -- ...
);

Entità

Task.java (estratto)
@Entity
@Table(name = "tasks")
public class Task {

    @Id @GeneratedValue
    private UUID id;

    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "project_id", nullable = false)
    private Project project;

    @Enumerated(EnumType.STRING)
    private TaskStatus status = TaskStatus.TODO;

    @Version
    private long version;
    // ...
}

Concetti chiave

  • UUID come PK — niente sequence, niente accoppiamento a un DB. Sicuro per esporre nei path.
  • @Enumerated(EnumType.STRING)mai ORDINAL: cambia se aggiungi enum nel mezzo.
  • fetch = LAZY sui @ManyToOne — evita di caricare il parent quando non serve. Vedi step-05 per N+1.
  • @Version — optimistic locking per concorrenza.
  • ddl-auto: validate — Flyway è l'unica fonte di verità per lo schema.

application.yml rilevanti

spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/sbfs
  jpa:
    hibernate:
      ddl-auto: validate
    open-in-view: false              # critical!
  flyway:
    enabled: true
    locations: classpath:db/migration

Esercizi

  1. Aggiungi un indice su (project_id, status) nella migration V2.
  2. Crea una query findTopByProject_IdOrderByDueDateAsc(UUID id) derivata.
  3. Spiega quando open-in-view: true (default!) è un problema.

Riferimenti nel codice