@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@Builder
@Getter
@Entity(name = "CustomerSeqId")
@Table(name="CUSTOMER", schema = "EX_IDSEQ")
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_customer")
@SequenceGenerator(name = "seq_customer", sequenceName = "CUSTOMER_SEQUENCE", allocationSize = 10)
private Long id;
private String name;
}Les clés primaires avec JPA
Concepts fondamentaux
- Clé primaire
- sous-ensemble minimal K d’attributs A = {a₁, a₂, …, aₙ} d’une relation R qui satisfait simultanément :
- l’unicité, qui garantit que pour tous tuples t₁, t₂ ∈ R, si t₁ ≠ t₂ alors leurs projections sur K sont différentes (t₁[K] ≠ t₂[K]),
- la minimalité, qui assure qu’aucun sous-ensemble propre K’ ⊂ K ne peut satisfaire la propriété d’unicité,
- la non-nullité, qui impose que pour tout tuple t ∈ R et tout attribut k ∈ K, la valeur t[k] ne peut pas être nulle (t[k] ≠ null),
- sous-ensemble minimal K d’attributs A = {a₁, a₂, …, aₙ} d’une relation R qui satisfait simultanément :
- Types supportés: primitifs (
int,long, …), wrappers (Integer,Long, …),String, Date (Date,LocalDate, …),UUID - Annotation
@Idsur l’attribut de l’entité
Génération automatique des Id atomiques
En théorie des bases de données relationnelles, une clé primaire est définie comme un sous-ensemble minimal K d’attributs A = {a₁, a₂, …, aₙ} d’une relation R qui satisfait simultanément trois propriétés fondamentales : (1) l’unicité, qui garantit que pour tous tuples t₁, t₂ ∈ R, si t₁ ≠ t₂ alors leurs projections sur K sont différentes (t₁[K] ≠ t₂[K]), (2) la minimalité, qui assure qu’aucun sous-ensemble propre K’ ⊂ K ne peut satisfaire la propriété d’unicité, et (3) la non-nullité, qui impose que pour tout tuple t ∈ R et tout attribut k ∈ K, la valeur t[k] ne peut pas être nulle (t[k] ≠ null), ces propriétés garantissant ainsi l’intégrité des entités et l’identification unique des tuples dans la base de données.
La génération automatique des identifiants est une fonctionnalité essentielle de JPA, permettant de déléguer la création des valeurs d’identifiants au framework.
Plusieurs stratégie de génération sont disponibles, chacune adaptée à un contexte particulier.
- IDENTITY
- Utilise l’auto-increment natif de la base
- Optimal pour MySQL/MariaDB
- Pas de batch possible
- SEQUENCE
- Utilise une séquence en base
- Performant avec allocationSize > 1
- Standard pour PostgreSQL/Oracle
- TABLE
- Utilise une table dédiée
- Portable mais moins performant
- Solution de dernier recours
- AUTO
- Choix automatique selon la base
- IDENTITY pour MySQL
- SEQUENCE pour PostgreSQL
- UUID
- Utilise un UUID
- Portable et unique
Tableau comparatif
| Stratégie | Performance | Portabilité | Batch | Scalabilité | Usage recommandé |
|---|---|---|---|---|---|
| IDENTITY | +++ | + | Non | + | MySQL/MariaDB |
| SEQUENCE | ++ | ++ | Oui | ++ | PostgreSQL/Oracle |
| TABLE | + | +++ | Oui | +++ | Multi-bases |
| AUTO | ++ | ++ | Selon | ++ | Développement |
| UUID | + | +++ | Non | ++++ | Systèmes distribués |
Notes sur la scalabilité:
- IDENTITY: problématique en sharding
- SEQUENCE: possible avec sequences par shard
- TABLE: adapté au clustering
- UUID: optimal pour systèmes distribués
Les générateurs SEQUENCE et IDENTITY
- Les générateurs SEQUENCE et IDENTITY spécifient l’utilisation d’une séquence de base de données ou d’une colonne d’identité (plus performant, mais pas portable). .
No JDBC connection available. Set system properties 'jdbc.url' (and optionally 'jdbc.user'/'jdbc.password'), or provide a Connection in the kernel environment.
Sequence et Batch
La génération de clés avec une séquence peut être optimisée en utilisant un allocationSize plus grand que 1. Cela permet de pré-allouer un bloc de valeurs pour les entités, réduisant ainsi le nombre d’appels à la séquence.
// Exemple de configuration optimisée
@Entity
public class Order {
@Id
@GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "order_seq"
)
@SequenceGenerator(
name = "order_seq",
sequenceName = "ORDER_SEQ",
initialValue = 1,
allocationSize = 50 // Optimisation batch
)
private Long id;
}générateur TABLE
Le générateur TABLE demande d’attribuer des clés primaires à l’entité en utilisant une table de base de données, c’est le plus portable mais le moins performant.
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@Builder
@Getter
@Entity(name = "CustomerTableId")
@Table(name="CUSTOMER", schema = "EX_IDTABLE")
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.TABLE, generator = "customer_table_generator")
@TableGenerator(name = "customer_table_generator", schema = "EX_IDTABLE", table = "ID_GENERATOR_TABLE")
private Long id;
private String name;
}No JDBC connection available. Set system properties 'jdbc.url' (and optionally 'jdbc.user'/'jdbc.password'), or provide a Connection in the kernel environment.
UUIDs
- Les UUIDs sont des identifiants universels uniques, générés aléatoirement.
- Avantages: unicité globale, distribution, sans coordination centrale
- Inconvénients: taille (16 octets), performance index, non séquentiel
- Pour la pagination ajouter un index sur un attribut date de création
- Usage: microservices, systèmes distribués, réplication multi-maîtres
@Getter
@Setter
@ToString
@RequiredArgsConstructor(staticName = "of")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
@Table(name = "CUSTOMER", schema = "EX_UUID")
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
private UUID uuid;
@Column(length = 50, nullable = false)
@NonNull
private String name;
}No JDBC connection available. Set system properties 'jdbc.url' (and optionally 'jdbc.user'/'jdbc.password'), or provide a Connection in the kernel environment.
Les clés composites
- Types de clés
- Clé atomique
- Natural (Clé naturelle) : attribut métier unique (email, SIRET)
- Surrogate (Clé artificielle) : identifiant technique unique (Long, UUID)
- Clé composite
- Natural : combinaison d’attributs métier (année + département + matricule)
- Surrogate : combinaison d’identifiants techniques (shardId + sequenceId)
- Clé atomique
- JPA propose deux approches pour gérer les clés composites:
@EmbeddedId: encapsule les attributs dans une classe dédiée@IdClass: distribue les attributs dans l’entité
Embedded Id
- Utilisation de l’annotation
@EmbeddedId - Encapsulation des attributs de la clé dans une classe dédiée
- La classe embarquée doit être annotée
@Embeddable- Doit implémenter Serializable
- Nécessite equals() et hashCode()
No JDBC connection available. Set system properties 'jdbc.url' (and optionally 'jdbc.user'/'jdbc.password'), or provide a Connection in the kernel environment.
@Getter
@Setter
@Entity(name="EmployeEmbeddedId")
@Table(name="EMPLOYE", schema = "EX_EMBEDDED_ID")
public class Employee {
@EmbeddedId
private EmployeePK id;
private String name;
}@EqualsAndHashCode
@Embeddable
@AllArgsConstructor
@NoArgsConstructor
public class EmployeePK implements Serializable {
private String department;
private int rankInDepartment;
}Id Class
- Utilisation de l’annotation
@IdClassdans l’entité. - Les attributs de la clé sont définis dans l’entité
- Une classe séparée définit la structure de la clé
- Les noms des attributs doivent correspondre
- Plus simple pour les requêtes JPQL
No JDBC connection available. Set system properties 'jdbc.url' (and optionally 'jdbc.user'/'jdbc.password'), or provide a Connection in the kernel environment.
@Setter
@Getter
@Entity(name="EmployeIdClass")
@Table(name="EMPLOYE", schema = "EX_IDCLASS")
@IdClass(EmployeePK.class)
public class Employee {
@Id
private String department;
@Id
private int rankInDepartment;
private String name;
}@EqualsAndHashCode
@Getter
@AllArgsConstructor
@NoArgsConstructor
public class EmployeePK implements Serializable {
@Id
private String department;
@Id
private int rankInDepartment;
}