Skip to content
Anjunar/ DOCS

Migrating at startup

The server migrates its database before it builds the SessionFactory. If anything is wrong, it does not start.

Integrator

The default: a Hibernate setting

With schema-integration on the class path, Hibernate settings switch the migration on. While Hibernate builds the SessionFactory, and before its own validation, the integrator reads the entities and migrates through one of Hibernate's connections.

properties
hibernate.ddl_manager.enabled=true hibernate.hbm2ddl.auto=validate hibernate.default_schema=public
Requirements
PostgreSQL 14 or newer and Hibernate ORM 7.4.
Every table and sequence has a schema, from @Table(schema) or hibernate.default_schema.
hbm2ddl.auto and schema-generation.database.action are unset, none or validate.
An unknown hibernate.ddl_manager setting is an error, so a typo cannot switch off a safeguard.
Explicit call

When the server owns the bootstrap

With JTA or multi-tenancy the integrator refuses to run. Call the migration yourself between building the metadata and building the SessionFactory, with a DataSource whose connections are not enlisted in JTA.

scala
import com.anjunar.hibernateddl.executor.ExecutionOptions import com.anjunar.hibernateddl.integration.HibernateSchemaMigration val metadata = MetadataSources(registry).addAnnotatedClass(classOf[Customer]).buildMetadata() val result = HibernateSchemaMigration.migrate(metadata, dataSource, ExecutionOptions(lockTimeoutMillis = 5000)) println(s"${result.status}: revision ${result.revision}, ${result.statementCount} statements") val sessionFactory = metadata.buildSessionFactory()
Output
Applied: revision 4, 3 statements

migrate runs synchronously. Any MigrationStatus lets the server continue; a MigrationException must abort the startup. Settings in the registry refine the options you pass.

History

No migration scripts, no snapshots

Every migration stores its target model as JSON in __hibernate_ddl.schema_history. The next start plans against that model. Because IDs are stable across all versions, a server may skip releases. Without history the previous model is empty, and all tables are created.

Applied
Changes were planned and executed; a new revision was written.
AlreadyApplied
The database already matches the target; only checked.
Adopted
A database without history matched the target and became revision 1.
ManuallyMigrated
A target migrated by hand was verified and recorded.
One transaction

What happens under the lock

Under a transactional advisory lock the executor checks the history, plans the changes, compares the database with the stored model, executes the DDL, checks the target and writes the new revision, all in one transaction.

Drift

Hand-made changes stop the start

A database that differs from the stored model is not migrated further.

Order

An older server cannot run

After a newer migration, an older model would rename or drop things back. It is refused.

Locks

Other nodes keep working

A start without changes only checks under a shared lock, so running nodes keep reading and writing.

Tables are locked while DDL runs

A migration with changes locks its tables exclusively until it commits. Plan large changes for a maintenance window.