Spring Boot Integration — Flyway/Liquibase, Auditing & Multi-Database Setups
How a schema actually evolves safely over a real application's lifetime, plus two other everyday integration concerns: automatic auditing and running against more than one database at once.
Learning objectives
- Explain why hibernate.hbm2ddl.auto=update is unsafe in production and what Flyway/Liquibase replace it with.
- Configure @EnableJpaAuditing to automatically populate creation/modification timestamps.
- Implement soft delete correctly, including consistent query filtering.
Every entity model in this course has assumed a database schema already exists, correctly — this chapter covers how that schema actually gets created and evolved safely over a real application's lifetime.
📖 Story
Imagine a house renovated over years — a room added, a wall moved — with no blueprint ever kept up to date, and no record of which change happened when. A new contractor has no reliable way to know the house's current structure. Here's the database equivalent, and why it's dangerous:
# DON'T do this in production: spring: jpa: hibernate: ddl-auto: update # Hibernate auto-generates/updates your schema
This is convenient in local development, but it can SILENTLY drop a column Hibernate thinks is no longer needed, with zero review process and zero awareness of your actual data. Here's the safe alternative:
-- V1__create_customers_table.sql (a Flyway migration) CREATE TABLE customers ( id BIGSERIAL PRIMARY KEY, name TEXT NOT NULL, email TEXT NOT NULL UNIQUE );
spring: jpa: hibernate: ddl-auto: validate # Hibernate only CHECKS the schema matches — never changes it flyway: enabled: true
Now every schema change is a reviewable, versioned, numbered SQL file — never a silent, automatic guess.
Flyway — a migration tool applying versioned SQL scripts in strict numbered order. Liquibase — a similar tool using XML/YAML changesets. @EnableJpaAuditing — enables automatic population of @CreatedDate/@LastModifiedDate fields. Soft delete — marking a row deleted rather than physically removing it.
Let's continue this chapter's customers table example with auditing and soft delete.
Auditing — automatic timestamps, zero manual code
@EntityListeners(AuditingEntityListener.class) @Entity public class Customer { @Id @GeneratedValue private Long id; @CreatedDate private Instant createdAt; @LastModifiedDate private Instant updatedAt; }
@Configuration @EnableJpaAuditing public class JpaConfig { }
With this in place, createdAt and updatedAt populate themselves automatically on every save/update — no explicit code needed anywhere in your service layer.
Soft delete — preserving history
@Entity public class Customer { @Id @GeneratedValue private Long id; private Instant deletedAt; // null = active public void softDelete() { this.deletedAt = Instant.now(); } } // Every "normal" query must remember to filter it out: @Query("SELECT c FROM Customer c WHERE c.deletedAt IS NULL") List<Customer> findAllActive();
Multi-database setup
An application reading from TWO genuinely separate databases needs two full sets of beans:
@Bean @Qualifier("primaryEntityManagerFactory") public LocalContainerEntityManagerFactoryBean primaryEntityManagerFactory(...) { ... } @Bean @Qualifier("reportingEntityManagerFactory") public LocalContainerEntityManagerFactoryBean reportingEntityManagerFactory(...) { ... }
Flyway maintains a metadata table (flyway_schema_history) recording every migration applied, its checksum, and when — on startup, it compares this against the migration files present, applying any not-yet-recorded ones in strict numeric order, and failing loudly if an already-applied migration's checksum has changed. @EnableJpaAuditing's timestamp population is implemented via a Hibernate entity listener hooked into the @PrePersist/@PreUpdate JPA lifecycle callbacks from Chapter 5 — it runs automatically, requiring no explicit service-layer code.
- Every real Spring Boot application in production uses Flyway or Liquibase as standard practice —
ddl-auto: updatein production is a red flag in almost any serious code review, exactly for the "silent column drop" risk this chapter's story describes. - A "trash/recycle bin" feature letting a user recover an accidentally deleted record is implemented via this chapter's soft-delete pattern.
- Use Flyway or Liquibase in every shared/production environment — never rely on
ddl-auto: update/create, exactly as this chapter's opening warning shows. - Keep migration files immutable once applied to any shared environment — add a new one for any further change.
- Enable
@EnableJpaAuditingas a standing default; it's essentially free.
⚠️ Why this keeps happening
ddl-auto: update feels genuinely convenient in early development — this exact convenience is what tempts teams to carry it into production, where its silent behavior becomes a real liability.
- Using
ddl-auto: update/createin production — this chapter's opening "DON'T do this" example. - Forgetting to filter soft-deleted rows in a NEW query, silently showing "deleted" records to users who shouldn't see them.
- Editing an already-applied Flyway migration file instead of adding a new one — Flyway detects this via checksum mismatch and fails loudly.
A migration adding a NOT NULL column to a very large existing table can require a full table rewrite, holding locks — usually handled in steps (add nullable, backfill, then add the constraint) for large production tables. Add a partial index on the not-deleted subset (WHERE deleted_at IS NULL) to keep soft-delete's filter cheap.
@CreatedBy/@LastModifiedBy auditing is itself security-relevant — a reliable audit trail is often a compliance requirement (GDPR, SOC 2), and getting the AuditorAware implementation wrong undermines its entire value.
Track migration execution time and success/failure in your deployment pipeline explicitly — a migration that silently fails during a deploy is a serious production risk.
Always test migrations against a staging environment with production-shaped data before applying to production — this catches both performance surprises and correctness surprises before they become an incident.
- Write and apply this chapter's exact
V1__create_customers_table.sqlFlyway migration, then a second migration adding a column, and confirm Flyway tracks both correctly. - Add
@CreatedDate/@LastModifiedDatewith@EnableJpaAuditing, save and update an entity, and confirm both timestamps populate with zero explicit code. - Implement this chapter's soft-delete pattern and confirm a soft-deleted record is excluded from
findAllActive()while still present in the table.
✓ Quick recap
- Flyway/Liquibase provide versioned, reviewable schema migrations — never rely on
ddl-auto: updatein production, exactly this chapter's warning. @EnableJpaAuditingautomatically populates timestamps via a standard JPA lifecycle callback.- Soft delete preserves history at the cost of needing consistent query filtering everywhere.
Want a visual for this concept?
Generate a diagram tailored to “Spring Boot Integration — Flyway/Liquibase, Auditing & Multi-Database Setups” — the AI picks whichever visual (flowchart, comparison, sequence, etc.) best fits.
Sign in to generate a visual →