Java.DBUpdate.What are best practices for naming Liquibase changesets?

Here are battle-tested naming practices that reduce conflicts, improve reviews, and make rollbacks/audits sane.


Goals of a good changeset name

A changeset identity in Liquibase is effectively:
(id + author + file path)

So good naming must:

  • be unique
  • be stable forever
  • be searchable
  • communicate intent

1) Use globally unique, descriptive IDs

Recommended patterns (pick one and standardize)

A) Timestamp + intent (very common in big teams)

  • 2026-01-15-1042-create-users-table
  • 2026-01-15-1108-add-status-to-orders

✅ Collision-resistant
✅ Sorts naturally
✅ Easy audit trail

B) Ticket ID + intent (best for traceability)

  • PAY-1423-add-index-orders-created_at
  • CORE-981-drop-legacy-flag

✅ Maps to work items
✅ Great for compliance

C) Domain prefix + sequence + intent (good for modules)

  • users-001-create-table
  • users-002-add-unique-email
  • orders-010-backfill-status

✅ Clear module ownership
⚠️ Requires discipline to avoid clashes

2) Keep author stable and meaningful

Use a team/service identifier, not a person (people leave, names change).

Examples:

  • author: payments-team
  • author: fin-analytics-service

If you must use individuals, use something stable:

  • author: stanley (ok, but less scalable)

3) One changeset = one intent

Bad:

  • id: update-schema
  • id: fixes

Good:

  • id: add-not-null-constraint-users-email
  • id: create-index-orders_status

This makes:

  • review easier
  • rollback reasoning possible
  • blame/traceability clean

4) Never rename IDs after they land

Once a changeset is merged and applied anywhere:

  • do not change id, author, or file location
  • that breaks history identity and causes validation issues / drift

Fix mistakes with a new changeset.

5) Prefer “what/why”, not “how”

Good ID:

  • add-unique-constraint-users-email

Less good:

  • alter-table-users-add-constraint-idx3

The tool already records the SQL; humans need intent.


6) Include rollout hints for risky changes

For large tables / backfills, encode intent:

  • orders-backfill-status-batch-1
  • expand-add-users_status-nullable
  • contract-drop-legacy_column

This maps nicely to expand–migrate–contract.


7) Be consistent with separators and casing

Pick one:

  • kebab-case: add-user-status
  • snake_case: add_user_status

Kebab-case tends to read best in PRs.


8) Avoid characters that cause tooling pain

Avoid spaces, #, ?, non-ASCII.
Stick to:

  • letters, numbers, -, _

Example “gold standard” (what I like in big teams)

- changeSet:
    id: PAY-1423-2026-01-15-add-index-orders-created-at
    author: payments-team
    changes:
      - createIndex:
          tableName: orders
          indexName: idx_orders_created_at
          columns:
            - column:
                name: created_at

Interview-ready answer (2 sentences)

Use stable, globally unique changeset IDs that describe intent, typically combining a ticket or timestamp with the change purpose, and keep author stable (often team/service). Never rename applied changesets, keep one intent per changeset, and use consistent casing/separators to reduce conflicts and improve auditability.

Posted in Без рубрики | Comments Off on Java.DBUpdate.What are best practices for naming Liquibase changesets?

Java.DBUpdate.How can you rollback only the last changeset?

In Liquibase, you roll back only the last executed changeset using a count-based rollback.


The direct answer (what interviewers want)

liquibase rollbackCount 1

This rolls back exactly the most recently applied changeset, using its explicit rollback definition (if present).

How this actually works (important)

  1. Liquibase looks at DATABASECHANGELOG
  2. Finds the last executed changeset
  3. Executes its <rollback> block
  4. Marks that changeset as rolled back

If the changeset does not define a rollback:

  • Liquibase will fail
  • or try an automatic rollback (very limited, often unsafe)

Example changeset with rollback

<changeSet id="add-status" author="stanley">
    <addColumn tableName="orders">
        <column name="status" type="varchar(20)"/>
    </addColumn>

    <rollback>
        <dropColumn tableName="orders" columnName="status"/>
    </rollback>
</changeSet>

Command:

liquibase rollbackCount 1

Alternative rollback strategies (know these)

Roll back to a tag

liquibase rollback release_1_2

Roll back to a date

liquibase rollbackToDate 2026-01-10T12:00:00

These are often safer than rollbackCount in real systems.

Critical production warning 🚨

Rolling back schema in production is dangerous once data has changed.

Senior teams usually prefer:

  • forward rollbacks (new compensating migration)
  • rollback commands only in dev / test / controlled releases

Interview-ready answer (2 sentences)

You can roll back the last changeset in Liquibase using rollbackCount 1, which executes the explicit rollback defined for the most recently applied changeset. In production, this is used sparingly, since rolling back schema after data changes is risky and teams usually fix forward instead.

Senior rule of thumb

If you’re afraid to read the rollback SQL, don’t run the rollback.

Posted in Без рубрики | Comments Off on Java.DBUpdate.How can you rollback only the last changeset?

Java.DBMigrationTools.What is the Liquibase updateSQL command used for?

updateSQL is a Liquibase “dry-run” command that shows you the exact SQL Liquibase would execute, without actually changing the database.

One-sentence definition (interview-ready)

updateSQL generates the SQL statements for pending changesets so you can review or approve them before execution.


What it’s used for (real-world)

1️⃣ Review & approval (very common in enterprises)

  • DBAs want to see SQL before it runs
  • Security/compliance requires approval
  • Teams want to understand locks, indexes, DDL
liquibase updateSQL

Outputs SQL instead of executing it.

2️⃣ Debug Liquibase abstractions

Liquibase is declarative — updateSQL lets you verify:

  • how types map per DB
  • which DDL is generated
  • whether something dangerous is happening

Example:

- addColumn:
    tableName: users
    columns:
      - column:
          name: status
          type: varchar(20)

On Postgres vs Oracle → SQL will differ.
updateSQL makes that explicit.

3️⃣ Controlled execution in restricted environments

Some orgs:

  • generate SQL with updateSQL
  • store it as an artifact
  • execute manually via DBA tools

Liquibase still tracks checksums/history.


What updateSQL does NOT do (important)

❌ Does not modify the database
❌ Does not insert rows into DATABASECHANGELOG
❌ Does not validate runtime behavior (locks, timing)

It’s preview only.

Typical workflow (enterprise pattern)

  1. Dev writes changelog
  2. CI runs: liquibase updateSQL > migration.sql
  3. SQL reviewed / approved
  4. Same changelog later executed with:
liquibase update

Common interview pitfalls ❌

“It applies migrations in a safe mode”
❌ No — it never applies anything.

“It validates migrations”
❌ No — it only prints SQL.

“It replaces staging testing”
❌ Absolutely not.


Interview-ready answer (2 sentences)

updateSQL is used to generate and preview the SQL Liquibase would run for pending changesets without executing them. It’s commonly used for review, auditing, and understanding database-specific SQL generated from declarative changelogs.


Senior rule of thumb

If you wouldn’t be comfortable reading the SQL from updateSQL, you shouldn’t run the migration in production.

Posted in Без рубрики | Comments Off on Java.DBMigrationTools.What is the Liquibase updateSQL command used for?

Java.DBMigrationTools.How do you split large changelogs into modular ones?

You split large Liquibase changelogs by composition, not chronology: one master changelog that includes many small, purpose-focused changelogs.

Core idea (interview-ready)

Use a single master changelog that includes modular changelogs by domain, feature, or layer, keeping each file small and owned.

1️⃣ The master changelog (entry point)

This is the only file Spring Boot / CI points to.

databaseChangeLog:
  - include:
      file: db/changelog/schema/schema.yaml
  - include:
      file: db/changelog/data/data.yaml
  - include:
      file: db/changelog/indexes/indexes.yaml

Why this works:

  • deterministic order
  • one migration history
  • easy to reason about execution flow

2️⃣ Split by responsibility (recommended)

Common, proven structure

db/changelog/
├── db.changelog-master.yaml
├── schema/
│   ├── users.yaml
│   ├── orders.yaml
│   └── payments.yaml
├── data/
│   ├── reference-data.yaml
│   └── backfills.yaml
├── indexes/
│   └── performance.yaml

Example: schema/users.yaml

databaseChangeLog:
  - changeSet:
      id: users-001-create-table
      author: stanley
      changes:
        - createTable:
            tableName: users
            columns:
              - column: { name: id, type: bigint, constraints: { primaryKey: true } }
              - column: { name: email, type: varchar(255), constraints: { nullable: false } }

Benefits:

  • smaller PRs
  • clearer ownership
  • fewer merge conflicts

3️⃣ include vs includeAll (important distinction)

✅ Prefer include

- include:
    file: db/changelog/schema/users.yaml

Why:

  • explicit order
  • predictable diffs
  • safe for production

⚠️ Be careful with includeAll

- includeAll:
    path: db/changelog/schema

Risks:

  • order depends on filesystem / build tool
  • easy to accidentally reorder migrations
  • harder to review

📌 Senior rule: includeAll is acceptable only for append-only, strictly ordered folders with clear naming.

4️⃣ Split by lifecycle, not by environment

❌ Bad split:

prod.yaml
dev.yaml
test.yaml

✅ Good split:

schema/
data/
indexes/

Environment differences belong in:

  • contexts
  • preconditions
  • runtime config
    —not in separate core changelogs.

5️⃣ Keep changesets small and atomic

Even inside modules:

  • one intent per changeset
  • avoid “mega changesets”
  • easier rollback reasoning
  • safer reviews

6️⃣ How this helps large teams

  • Teams own their module file
  • Fewer merge conflicts
  • Clear review responsibility
  • Safer parallel work
  • Easier long-term maintenance

Interview-ready answer (2–3 sentences)

Large changelogs are split by using a single master changelog that includes multiple smaller, purpose-focused changelogs, typically organized by domain or lifecycle like schema, data, and indexes. This keeps migrations readable, reduces merge conflicts, and preserves deterministic execution order.

Senior rule of thumb (remember this)

One master changelog, many small modules, explicit includes, and no environment-driven schema splits.

Posted in Без рубрики | Comments Off on Java.DBMigrationTools.How do you split large changelogs into modular ones?

Java.DBMigrationTools.What is the difference between YAML and XML changelogs?

Short answer: there’s no functional difference — only readability, verbosity, and team preference.
Liquibase treats YAML and XML identically after parsing.


Core truth (interview-ready)

YAML and XML changelogs express the same Liquibase model; the difference is syntax, not capability.


Side-by-side comparison

Same change — XML

<databaseChangeLog>
  <changeSet id="create-users" author="stanley">
    <createTable tableName="users">
      <column name="id" type="bigint" autoIncrement="true">
        <constraints primaryKey="true"/>
      </column>
      <column name="email" type="varchar(255)">
        <constraints nullable="false" unique="true"/>
      </column>
    </createTable>
  </changeSet>
</databaseChangeLog>

Same change — YAML

databaseChangeLog:
  - changeSet:
      id: create-users
      author: stanley
      changes:
        - createTable:
            tableName: users
            columns:
              - column:
                  name: id
                  type: bigint
                  autoIncrement: true
                  constraints:
                    primaryKey: true
              - column:
                  name: email
                  type: varchar(255)
                  constraints:
                    nullable: false
                    unique: true

Practical differences that matter in teams

AspectYAMLXML
Verbosity✅ Less❌ More
Readability✅ Better for humans⚠️ Noisy
Diff / PR review✅ Cleaner❌ Harder
Tooling maturity⚠️ Good✅ Excellent
Strictness❌ Indentation-sensitive✅ Very strict
Schema validation❌ Weaker✅ Strong (XSD)

Pros & cons in real projects

YAML — why teams choose it

✅ Cleaner PRs
✅ Faster to write
✅ Less boilerplate
✅ Preferred by product teams

⚠️ Indentation errors can be subtle
⚠️ Less strict validation


XML — why enterprises still use it

✅ Strong schema validation (XSD)
✅ Clear structure for complex changelogs
✅ Better IDE auto-complete in some setups
✅ Familiar to long-time Liquibase users

❌ Verbose
❌ Harder to review
❌ Merge conflicts are uglier


What does not change (important)

No difference in:

  • execution order
  • rollbacks
  • contexts / labels
  • preconditions
  • checksums
  • safety guarantees

Liquibase converts both into the same internal representation.


Interview pitfalls ❌

  • “YAML is less powerful than XML” → wrong
  • “XML is safer by default” → misleading
  • “They run differently” → false

Interview-ready answer (2 sentences)

YAML and XML changelogs are functionally equivalent in Liquibase; they differ only in syntax and readability. YAML is usually preferred for cleaner diffs and faster authoring, while XML offers stricter validation and is common in older or highly regulated projects.

Senior rule of thumb

Choose YAML for humans, XML for tools — but don’t mix both without a clear convention.

Posted in Без рубрики | Comments Off on Java.DBMigrationTools.What is the difference between YAML and XML changelogs?

Java.DBMigrationTools.How do preconditions improve the safety of a changelog?

Preconditions make a changelog defensive: they verify assumptions about the database before a changeset runs, preventing unsafe or invalid migrations.

One-sentence definition (interview-ready)

Preconditions are checks evaluated before a changeset executes that ensure the database is in the expected state, otherwise Liquibase stops or alters execution safely.

What problem preconditions solve

Without preconditions:

  • migrations assume the DB looks a certain way
  • drift or partial history causes runtime failure
  • destructive SQL may run blindly

With preconditions:

  • Liquibase asks first, then changes

How preconditions improve safety

1️⃣ Prevent destructive or invalid operations

Example: don’t drop a column that doesn’t exist

<preConditions onFail="HALT">
    <columnExists tableName="users" columnName="email"/>
</preConditions>

Without this:

ALTER TABLE users DROP COLUMN email;
-- fails or behaves unexpectedly

2️⃣ Protect against environment drift

Example: only run if table exists (maybe legacy DB)

<preConditions onFail="MARK_RAN">
    <tableExists tableName="legacy_users"/>
</preConditions>

If table is missing:

  • changeset is marked as executed
  • no failure
  • pipeline continues

3️⃣ Avoid partial production outages

Preconditions can stop deploy before risky DDL runs.

<preConditions onFail="HALT">
    <dbms type="postgresql"/>
</preConditions>

4️⃣ Enable idempotent and repeatable behavior

Preconditions let you safely express:

  • “run this only if needed”
  • “skip if already applied”

Example:

<preConditions onFail="MARK_RAN">
    <not>
        <indexExists indexName="idx_users_email"/>
    </not>
</preConditions>

onFail strategies (this matters in interviews)

onFailMeaningWhen to use
HALTStop migrationCritical schema safety
MARK_RANSkip but recordLegacy / optional changes
WARNLog warningObservability only
CONTINUEIgnore failureRare, risky

🚨 MARK_RAN is powerful but dangerous — use sparingly.


What preconditions are NOT

❌ Not a replacement for proper versioning
❌ Not a way to hide required schema changes
❌ Not for business logic checks


Interview pitfalls ❌

  • “We use preconditions to make migrations optional everywhere”
  • “MARK_RAN is safe by default”
  • “Preconditions replace migration ordering”

All red flags.


Interview-ready answer (2–3 sentences)

Preconditions improve changelog safety by validating database assumptions before executing a changeset, preventing destructive or invalid operations. They allow Liquibase to halt, skip, or warn based on controlled failure modes, which is especially useful for handling legacy schemas and environment drift safely.


Senior rule of thumb

Preconditions turn migrations from blind scripts into defensive operations.

Posted in Без рубрики | Comments Off on Java.DBMigrationTools.How do preconditions improve the safety of a changelog?

Java.DBMigrationTools.What are labels in Liquibase and why use them?

Labels in Liquibase are a filtering mechanism that lets you group changesets by purpose and selectively run them, independent of environment.

One-sentence definition (interview-ready)

Labels are logical tags on Liquibase changesets that let you include or exclude migrations based on intent (feature, module, release), not environment.


Why labels exist (the problem they solve)

Contexts answer “where should this run?”
Labels answer “which logical changes should run?”

Labels are useful when you want to:

  • run only migrations for a feature or module
  • control phased releases (e.g., infra vs business)
  • support selective execution without branching changelogs

How labels work (mechanics)

1️⃣ Add a label to a changeset

<changeSet id="add-user-status" author="stanley" labels="user,feature-x">
    <addColumn tableName="users">
        <column name="status" type="varchar(20)"/>
    </addColumn>
</changeSet>

2️⃣ Activate labels at runtime

liquibase update --labels=feature-x

Or in Spring Boot:

spring:
  liquibase:
    labels: feature-x

Only changesets whose labels match the expression will run.

Matching rules (important)

  • Labels are ORed by default: user,feature-x
  • You can use expressions:
    • feature-x AND user
    • infra AND !prod-risky

Liquibase evaluates this before execution.


Labels vs Contexts (clear distinction)

AspectContextsLabels
PurposeEnvironment controlLogical grouping
Typical valuesdev, test, prodfeature-x, infra, hotfix
Who sets itOps / runtimeMigration author
Should gate schema?❌ No❌ Usually no
Best forEnv-specific dataSelective runs, releases

Most teams use contexts rarely and labels even more rarely—but labels shine in large, coordinated releases.


Good use cases for labels ✅

  • Running only infra changes first
  • Splitting a big release into phases
  • Multi-module repos where teams share a DB
  • Dry-runs / previews of specific migration sets

Bad use cases (red flags 🚨)

  • Hiding required schema changes
  • Making prod schema differ from dev/test
  • Replacing proper versioning with labels

Interview-ready answer (2 sentences)

Labels in Liquibase are logical tags that allow selective execution of changesets based on purpose, such as features or modules, rather than environment. They’re useful for phased releases or large teams, but core schema migrations should remain label-free so all environments stay consistent.

Senior rule of thumb

If the app needs the schema change to run, don’t hide it behind a label.

Because labels make schema changes conditional, and conditional schema = environment drift + runtime failures.

Let’s unpack this like a senior reviewer would.


Core reason (one sentence)

If a schema change is required for the app to run, hiding it behind a label means some environments may not have that schema — and the app will break unpredictably.


What actually goes wrong (step by step)

Scenario

You add a column:

<changeSet id="add-status" author="stanley" labels="feature-x">
    <addColumn tableName="orders">
        <column name="status" type="varchar(20)"/>
    </addColumn>
</changeSet>

App code now does:

SELECT status FROM orders;

Environment outcomes

EnvLabels activeColumn exists?App
devfeature-x✅Works
staging❌❌Fails
prod❌❌Fails

Same code, different DB shape.

🚨 You’ve broken the core invariant:

“All environments share the same schema evolution.”


Why this is worse than it looks

1️⃣ Failures are delayed and non-obvious

  • CI may pass
  • Dev works
  • Prod crashes on first query

These are the hardest incidents to debug.


2️⃣ Rolling deployments amplify the damage

Some nodes:

  • expect new schema
    Others:
  • don’t have it

Result:

  • intermittent failures
  • retries
  • corrupted async flows

3️⃣ You can’t reason about the DB anymore

Questions like:

  • “Does orders.status exist?”
    Answer becomes:

“Depends on labels.”

That’s operational hell.


When labels are acceptable

Safe uses

  • Seed/demo data
  • Maintenance helpers
  • Permission grants
  • Derived objects (views, functions)

These do not affect app correctness.


What to do instead (the right pattern)

Use backward-compatible migrations

Don’t gate the schema — gate usage in the app.

Example:

  1. Migration (always runs):
ALTER TABLE orders ADD COLUMN status VARCHAR(20);
  1. App reads column only after feature flag enabled
  2. Later, enforce NOT NULL / constraints

Schema evolves safely everywhere.


Interview-ready answer (2 sentences)

Labels make migrations conditional, which can cause different environments to end up with different schemas.
If application code depends on a schema change, it must run everywhere to preserve determinism; only optional or non-critical changes should be gated by labels.


Senior mental model (remember this)

Application logic may be conditional; database schema must not be.

Posted in Без рубрики | Comments Off on Java.DBMigrationTools.What are labels in Liquibase and why use them?

Java.DBMigrationTools.What are contexts and how are they used?

Contexts are a Liquibase feature that lets you control which changesets run in which environment or situation.


One-sentence definition (interview-ready)

Contexts are tags on Liquibase changesets that determine whether a change runs based on the active environment (dev, test, prod, etc.).


What problem contexts solve

Without contexts:

  • every changeset runs everywhere
  • dev/test-only data or helpers accidentally reach prod

With contexts:

  • you scope migrations intentionally

Typical needs:

  • test data only in dev
  • debug helpers only locally
  • prod-only hardening steps

How contexts work (mechanics)

1️⃣ Define context on a changeset

<changeSet id="seed-test-users" author="stanley" context="dev,test">
    <insert tableName="users">
        <column name="email" value="test@example.com"/>
    </insert>
</changeSet>

This changeset runs only if the active context matches dev or test.


2️⃣ Activate contexts at runtime

In Spring Boot:

spring:
  liquibase:
    contexts: dev

Or via CLI:

liquibase update --contexts=prod

3️⃣ Matching rules (important)

  • Contexts are ORed by default
    (dev,test → runs in dev or test)
  • You can use expressions:
    • dev AND !prod
    • test,staging

Liquibase evaluates this before executing a changeset.

Typical real-world use cases

✅ Good uses

  • Seed/demo data
  • Test fixtures
  • Debug helpers
  • Environment-specific permissions
  • Temporary safety checks

❌ Bad uses (red flags 🚨)

  • Core schema changes
  • Table/column creation
  • Business-critical data migrations

Schema evolution should be environment-independent.


Contexts vs labels (quick clarity)

FeatureContextsLabels
PurposeEnvironment controlLogical grouping
Typical meaningdev / test / prodfeature-x / hotfix
Who sets itRuntime configMigration author

(You can combine them, but most teams don’t need both.)


Interview pitfall to avoid ❌

“We use contexts to decide which schema changes go to prod.”

That signals fragile migrations.

Correct framing:

Contexts are for optional or environment-specific changes, not core schema evolution.


Interview-ready answer (2 sentences)

Contexts in Liquibase are environment tags on changesets that control whether they execute based on the active runtime context. They’re commonly used for dev/test data or environment-specific operations, while core schema migrations remain context-free and always run.


Senior rule of thumb

If a schema change must exist for the app to work, it should not be hidden behind a context.

Posted in Без рубрики | Comments Off on Java.DBMigrationTools.What are contexts and how are they used?

Java.DBMigrationTools.What is Vacuum in postgres?

The short, correct definition (interview-ready)

VACUUM removes dead tuples created by updates/deletes so PostgreSQL can reuse space and keep queries fast.


Why VACUUM exists (MVCC in 30 seconds)

Postgres uses MVCC:

  • UPDATE doesn’t overwrite a row → it creates a new version
  • DELETE marks rows as invisible
  • Old versions (dead tuples) remain on disk

Without VACUUM:

  • tables bloat
  • indexes bloat
  • queries slow down
  • transaction ID wraparound risk appears (catastrophic)

What VACUUM actually does

  • Marks dead tuples as reusable
  • Updates visibility maps
  • Helps planner statistics (partially)
  • Prevents transaction ID wraparound

⚠️ It does not shrink the table file (that’s a common misconception)


Types of VACUUM

1) AUTOVACUUM (the default, should always be ON)

  • Background workers
  • Runs automatically based on thresholds
  • Non-blocking for normal queries
SHOW autovacuum;

✅ Safe for production
✅ Always enabled in sane setups


2) VACUUM

Manual, lightweight cleanup.

VACUUM orders;
  • Frees space inside the table
  • Doesn’t lock reads/writes
  • Does not reduce disk size

3) VACUUM FULL (dangerous)

VACUUM FULL orders;
  • Rewrites the entire table
  • Exclusive lock (blocks reads & writes)
  • Physically shrinks table on disk

❌ Rarely used in production
❌ Often causes downtime


What VACUUM is not

❌ Not a migration
❌ Not transactional
❌ Not reversible
❌ Not something you run inside Flyway/Liquibase migrations

VACUUM cannot run inside a transaction block.


Why migrations care about VACUUM

  • Large data migrations create many dead tuples
  • Index rebuilds + updates can cause bloat
  • VACUUM is often scheduled after heavy migrations
  • Never mix it into schema migrations

Correct pattern:

  1. Deploy migration
  2. Let autovacuum do its job
  3. Manually tune or vacuum off-hours if needed

Common interview pitfalls ❌

  • “VACUUM deletes rows” → wrong
  • “VACUUM frees disk space” → usually wrong
  • “You should VACUUM in migrations” → very wrong

When you do think about VACUUM explicitly

  • Huge update/delete jobs
  • Tables with frequent churn
  • Long-running transactions blocking autovacuum
  • Sudden table bloat

One-sentence senior takeaway

VACUUM is PostgreSQL’s garbage collector for MVCC, reclaiming dead row versions so space and performance don’t degrade over time.

Posted in Без рубрики | Comments Off on Java.DBMigrationTools.What is Vacuum in postgres?

Java.DBMigrationTools.ManyDataSourcesExample

application.yml

spring:
  datasource:
    orders:
      url: jdbc:postgresql://localhost:5432/orders_db
      username: orders_user
      password: secret
      driver-class-name: org.postgresql.Driver
    billing:
      url: jdbc:postgresql://localhost:5432/billing_db
      username: billing_user
      password: secret
      driver-class-name: org.postgresql.Driver

  jpa:
    hibernate:
      ddl-auto: validate   # important: migrations own schema

  liquibase:
    enabled: false         # disable default single-datasource auto-config

DataSource + Liquibase beans (Java)

package com.example.config;

import javax.sql.DataSource;

import liquibase.integration.spring.SpringLiquibase;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MultiDbLiquibaseConfig {

    // ----------- DataSources -----------

    @Bean(name = "ordersDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.orders")
    public DataSource ordersDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "billingDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.billing")
    public DataSource billingDataSource() {
        return DataSourceBuilder.create().build();
    }

    // ----------- Liquibase per DB -----------

    @Bean
    public SpringLiquibase ordersLiquibase(@Qualifier("ordersDataSource") DataSource ds) {
        SpringLiquibase lb = new SpringLiquibase();
        lb.setDataSource(ds);
        lb.setChangeLog("classpath:db/changelog/orders/db.changelog-master.yaml");
        lb.setContexts("prod"); // optional
        lb.setShouldRun(true);
        return lb;
    }

    @Bean
    public SpringLiquibase billingLiquibase(@Qualifier("billingDataSource") DataSource ds) {
        SpringLiquibase lb = new SpringLiquibase();
        lb.setDataSource(ds);
        lb.setChangeLog("classpath:db/changelog/billing/db.changelog-master.yaml");
        lb.setContexts("prod"); // optional
        lb.setShouldRun(true);
        return lb;
    }
}

Folder structure example:

src/main/resources/db/changelog/orders/db.changelog-master.yaml
src/main/resources/db/changelog/billing/db.changelog-master.yaml

Why disable spring.liquibase.enabled?

Spring Boot’s default Liquibase auto-config assumes one datasource. With multiple DBs, you want explicit control so:

  • each changelog targets the right DB
  • failures are obvious
  • no accidental migrations against the wrong schema

Interview tip (what to say)

“In multi-datasource apps, I disable the default Liquibase auto-config and define one SpringLiquibase bean per datasource, each pointing to its own changelog. I also set Hibernate ddl-auto=validate so schema changes only come from migrations.”

Posted in Без рубрики | Comments Off on Java.DBMigrationTools.ManyDataSourcesExample