# Primary Keys and ID Generation — Hibernate / JPA

Source: https://www.skillbyai.com/en/hibernate-jpa/m-keys

> Choose ID generation strategies and map natural and composite keys.

## Identifiers and how they are generated

Every entity needs a primary key. **`@GeneratedValue`** strategies: **`SEQUENCE`** uses a database sequence and is the best choice where supported (PostgreSQL, Oracle, SQL Server, MariaDB, H2); with an **`allocationSize`** (for example 50) Hibernate reserves blocks of IDs and avoids a round trip per insert, and IDs are known before the INSERT, enabling **JDBC batching**. **`IDENTITY`** uses auto-increment columns (common in MySQL); the ID is known only after the INSERT, so Hibernate must insert immediately on `persist` and **cannot batch inserts**. **`TABLE`** emulates sequences with a table and scales poorly. **`AUTO`** lets the provider choose. **`UUID`** (JPA 3.1+, or Hibernate's `@UuidGenerator`) generates identifiers in the application, convenient for distributed systems and for creating IDs before persisting; time-ordered UUIDs (version 7) index better than random version 4. Business keys such as an ISBN or email can be mapped with `@NaturalId` and a unique constraint while keeping a surrogate primary key. **Composite keys** use `@EmbeddedId` or `@IdClass`, mostly for join tables and legacy schemas.

## Sequence, UUID and composite keys

Sequence IDs with block allocation, application-generated UUIDs and an embedded composite key.

```java
@Entity
public class Invoice {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "invoice_seq")
    @SequenceGenerator(name = "invoice_seq", sequenceName = "invoices_seq", allocationSize = 50)
    private Long id;                     // batch-friendly; matches INCREMENT BY 50 in the DB
}

@Entity
public class Device {
    @Id
    @GeneratedValue(strategy = GenerationType.UUID)   // JPA 3.1+
    private UUID id;

    @NaturalId                              // Hibernate: business key, still unique in DB
    @Column(nullable = false, unique = true)
    private String serialNumber;
}

@Embeddable
public record EnrolmentId(Long studentId, String courseCode) implements Serializable { }

@Entity
public class Enrolment {
    @EmbeddedId
    private EnrolmentId id;
    private String grade;
}
```

## Token numbers at a counter

A sequence with an allocation size is like a clerk who collects a booklet of 50 token numbers at once instead of walking back to the dispenser for each customer. IDENTITY is getting each number only after the customer is already seated.

**Quiz:** Why can IDENTITY ID generation prevent Hibernate from batching inserts?

- [ ] IDENTITY IDs are random
- [ ] IDENTITY is not supported by JDBC
- [ ] Batching requires UUIDs
- [x] The ID is only known after each INSERT executes, so Hibernate must insert immediately on persist

*Answer:* The ID is only known after each INSERT executes, so Hibernate must insert immediately on persist. Hibernate needs the generated key right away, forcing one INSERT per persist.
