Entities et relations avec un ORM
Décorateurs d’entités et leur traduction SQL exacte.
Objectifs
À la fin de cette leçon, vous saurez :
- déclarer une entité complète avec ses colonnes et contraintes ;
- modéliser les relations one-to-many et many-to-one ;
- synchroniser le schéma proprement (jamais en aveugle).
🔗 Pour vous rafraîchir la mémoire : les bases TypeScript · modules : import/export
L'entité : le schéma vivant en TypeScript
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, CreateDateColumn } from "typeorm";
@Entity()
export class Task {
@PrimaryGeneratedColumn()
id: number;
@Column({ length: 200 })
titre: string;
@Column({ default: false })
fait: boolean;
@CreateDateColumn()
creeLe: Date;
@ManyToOne(() => User, (user) => user.tasks, { nullable: false })
user: User;
}
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column({ unique: true })
email: string;
@OneToMany(() => Task, (task) => task.user)
tasks: Task[];
}
Correspondances à lire dans les deux sens :
| Décorateur | SQL généré |
|---|---|
@PrimaryGeneratedColumn() | id integer GENERATED ... PRIMARY KEY |
@Column({ length: 200 }) | titre varchar(200) |
@Column({ default: false }) | fait boolean DEFAULT false |
@ManyToOne(...) | FK taskId integer REFERENCES user(id) |
@CreateDateColumn() | creeLe timestamptz DEFAULT now() |
La relation ManyToOne côté Task + OneToMany côté User décrivent la MÊME FK : l'ORM stocke la colonne du côté « many » uniquement. Le champ user de Task ne contient pas une ligne : il porte la clé étrangère et sert aux chargements relationnels.
Ce que vous devez reconnaître
Chaque décorateur correspond à une décision que vous avez prise à la main au chapitre 20 :
unique: true→ votreUNIQUEsur email ;nullable: false→ votreNOT NULL;- la relation → votre
REFERENCES users(id).
L'ORM n'invente rien : il écrit VOTRE schéma depuis des classes. Si vous ne savez pas quel SQL découlera d'un décorateur, c'est un signal : relisez le chapitre SQL avant d'utiliser ce décorateur.
Synchroniser le schéma : jamais en aveugle
// DÉVELOPPEMENT UNIQUEMENT — dangereux ailleurs
await dataSource.synchronize(true); // aligne la base sur les entités
synchronize peut supprimer des colonnes pour « aligner » : acceptable sur une base de dev jetable, catastrophique en production. La voie professionnelle est la migration — objet de la troisième leçon du chapitre.
Exercice
- Écrivez les deux entités ci-dessus et connectez-vous à une base de test.
- Activez le logging (
logging: true) et observez le CREATE TABLE généré. - Comparez-le ligne par ligne avec celui que vous écririez à la main.
- Ajoutez
@Index()surtitre: quel ALTER TABLE apparaît ?
Résumé
- Entité = table décrite en classe ; relations = FK déclarée deux fois, stockée une fois.
- Chaque décorateur a une traduction SQL que vous savez prédire.
- synchronize = dev seulement ; production = migrations.
Correction disponibleCherchez d’abord par vous-même.Voir la correction
Correction
Réponses détaillées
Question 4. Un CREATE INDEX IDX_... ON task (titre). À méditer : cet index est-il justifié ? Vous cherchez par titre ? Sinon, il ne coûte que des écritures — le décorateur n'enlève pas le jugement.