Skip to content
Anjunar/ DOCS

Stable IDs

A name can change, an ID never does. That is how the framework tells a rename from a drop and a create.

Why

Hibernate sees names, the database needs identity

Between two versions of an entity, a renamed column looks like one column gone and another one new. Without identity every rename would drop data. @SchemaId gives each table and column an identity that survives renames.

scala
@Entity @SchemaId("7f3a9c21") @Table(name = "customer") class Customer: @Id @SchemaId("0a1b2c3d") var id: java.lang.Long = uninitialized @SchemaId("f34e45b6") @Column(name = "nick_name") var nickName: String = uninitialized @Embedded @SchemaId("3c4d5e6f") var address: Address = uninitialized @Embeddable class Address: @SchemaId("9a8b7c6d") var city: String = uninitialized
Rules

Eight hex digits, once, forever

An ID is eight lowercase hex digits, generated at random. Entity IDs are unique across entities, property IDs within their entity.

What to keep in mind
Generate the ID at random; never derive it from a name.
Never change an ID, and never reuse one: a dropped ID is retired for good.
Copying an entity copies its IDs. Give the copy new ones.
Properties inside a jsonb column need no ID: the framework manages the column, not the document.
Addressing

How IDs name tables and columns

Approvals, backfills and error messages address a table or column by a path of IDs. A table uses its entity's ID, a column adds the property's.

7f3a9c21
The table of the entity.
7f3a9c21/f34e45b6
A column of that table.
7f3a9c21/3c4d5e6f/9a8b7c6d
A column of an embedded value.
7f3a9c21/5b6c7d8e
A collection table (element collection or many-to-many join table) takes the ID of its property.

Inheritance keeps these IDs: a single-table hierarchy shares its root's table, joined and table-per-class subclasses have their own. A join column is a column like any other.

Secondary tables

One more ID per secondary table

@SecondaryTableId names the table like @SecondaryTable does. To rename the secondary table, change both names and keep the value.

scala
@Entity @SchemaId("7f3a9c21") @SecondaryTable(name = "customer_details") @SecondaryTableId(table = "customer_details", value = "5d6e7f80") class Customer
Mistakes

Missing IDs stop the start and suggest one

HibernateSchemaSource reads the boot model and returns every problem at once. A missing or invalid ID comes with a freshly generated suggestion.

scala
import com.anjunar.hibernateddl.hibernate.HibernateSchemaSource HibernateSchemaSource.read(metadata) match case Left(errors) => errors.foreach(println) case Right(model) => println(s"${model.tables.size} tables")
Output
Customer.email has no @SchemaId; add e.g. @SchemaId("c41d2e0f")