Schema Generation (hbm2ddl)
Intermediatespring.jpa.hibernate.ddl-auto controls schema generation: validate (production), update (development), create-drop (tests); use Flyway or Liquibase for production migrations.
Overview
Hibernate's DDL auto feature (hbm2ddl.auto) can generate, update, validate, or drop/recreate the database schema from JPA entity mappings at startup. In development it is convenient; in production it is dangerous — update mode can silently drop columns or leave orphaned tables, and neither mode provides rollback capability, version history, or team collaboration. The industry-standard approach is to set ddl-auto=validate in production (ensuring the DB schema matches entity mappings without modifying anything) and manage all schema changes with a migration tool (Flyway or Liquibase). These tools version-control SQL scripts, apply them in order, and prevent the same script from running twice, enabling safe automated deployments and rollbacks.
ddl-auto modes and when to use them
Each mode has specific use-cases. Using the wrong mode in the wrong environment is a common source of data loss or startup failures.
# application.properties
# PRODUCTION: validate entity mappings against existing schema, fail fast if mismatch
spring.jpa.hibernate.ddl-auto=validate
# DEVELOPMENT: let Hibernate update schema (adds columns, creates tables)
# WARNING: never on production — update can drop NOT NULL or rename incorrectly
spring.jpa.hibernate.ddl-auto=update
# TESTING: drop and recreate schema before each test run
spring.jpa.hibernate.ddl-auto=create-drop
# CI/TESTING ALTERNATIVE: create schema fresh (no drop on close)
spring.jpa.hibernate.ddl-auto=create
# NONE: disable Hibernate DDL entirely (use with Flyway/Liquibase)
spring.jpa.hibernate.ddl-auto=none
# Show generated DDL (useful during development)
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
# Export Hibernate-generated DDL to file without executing
spring.jpa.properties.javax.persistence.schema-generation.scripts.action=create
spring.jpa.properties.javax.persistence.schema-generation.scripts.create-target=schema.sqlFlyway for production schema migrations
Flyway versioned migrations are the standard for production. Scripts in src/main/resources/db/migration are applied in version order exactly once. Spring Boot auto-configures Flyway when it is on the classpath.
<!-- pom.xml -->
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
<!-- For MySQL 8+ -->
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-mysql</artifactId>
</dependency>
# application.properties — Flyway config
spring.flyway.enabled=true
spring.flyway.baseline-on-migrate=true # first run on existing DB
spring.flyway.locations=classpath:db/migration
spring.jpa.hibernate.ddl-auto=validate # Flyway handles DDL, Hibernate validates
# Migration files: src/main/resources/db/migration/
# V1__create_orders_table.sql
CREATE TABLE orders (
id BIGINT NOT NULL AUTO_INCREMENT,
customer_id BIGINT NOT NULL,
status VARCHAR(30) NOT NULL DEFAULT 'PENDING',
total DECIMAL(10,2) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
INDEX idx_customer (customer_id)
);
# V2__add_order_reference.sql
ALTER TABLE orders ADD COLUMN reference VARCHAR(50) UNIQUE;
# V3__add_items_table.sql
CREATE TABLE order_items ( ... );Generating Flyway baseline migration from entities
A common workflow: let Hibernate generate the initial DDL from entities, then hand it to Flyway as V1. Use the ddl export property to capture the SQL without executing it.
# Step 1: export Hibernate DDL to file (no DB execution)
# application-ddl-export.properties
spring.jpa.hibernate.ddl-auto=none
spring.datasource.url=jdbc:h2:mem:testdb
spring.jpa.properties.javax.persistence.schema-generation.scripts.action=create
spring.jpa.properties.javax.persistence.schema-generation.create-source=metadata
spring.jpa.properties.javax.persistence.schema-generation.scripts.create-target= src/main/resources/db/migration/V1__initial_schema.sql
# Step 2: review generated SQL, clean it up, add indexes
# Step 3: set spring.flyway.baseline-on-migrate=true for first deploy
# Useful: validate Flyway + Hibernate consistency in tests
@SpringBootTest
@Testcontainers
class SchemaConsistencyTest {
@Container
static MySQLContainer<?> mysql = new MySQLContainer<>("mysql:8");
@Test
void flywayAndHibernateSchemaShouldMatch() {
// If Flyway runs cleanly AND Hibernate validates (ddl-auto=validate)
// without exception, schemas are consistent.
// Spring Boot test context startup itself is the assertion.
}
}Key Points to Remember
- 1Never use ddl-auto=update or create-drop in production — use validate or none only.
- 2Flyway (or Liquibase) version-controls schema changes, enables rollbacks, and prevents double-application of scripts.
- 3Spring Boot auto-configures Flyway; add flyway-core to pom.xml and put scripts in db/migration.
- 4Naming convention: V{version}__{description}.sql — double underscore, no spaces.
- 5Use baseline-on-migrate=true when introducing Flyway to an existing database.
- 6Testcontainers + Flyway in tests gives you a real DB migration smoke test on every build.
Interview Questions
Sign in to ask AriaWhy is ddl-auto=update dangerous in production even though it seems convenient?
How does Flyway track which migrations have already been applied?
What is the difference between Flyway versioned (V) and repeatable (R) migration scripts?
How would you safely add a NOT NULL column to a large production table without downtime?
How do you validate that your Flyway migrations and JPA entity mappings are consistent?
Ask Aria about Schema Generation (hbm2ddl)
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.