Spring Boot + MySQL (running in Docker)¶
π Prereq: MySQL container is up (see [Docker setup link]).
1. Add MySQL driver¶
Maven (pom.xml):
Gradle (build.gradle):
2. Configure datasource¶
src/main/resources/application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/appdb
spring.datasource.username=appuser
spring.datasource.password=apppass
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# Choose ONE ddl-auto option (see below)
spring.jpa.hibernate.ddl-auto=update
# Tells Hibernate which SQL dialect to use so it can generate the correct SQL syntax
# (MySQL 8 has features and keywords not in older versions, so use MySQL8Dialect).
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect
spring.jpa.show-sql=true # optional, for debugging
Alternative (YAML):
spring:
datasource:
url: jdbc:mysql://localhost:3306/appdb
username: appuser
password: apppass
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: update # or validate/create-drop
show-sql: true
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL8Dialect
3. Schema management options¶
Spring Boot can handle schema in several ways. Pick one strategy:
A) JPA auto-DDL (fast dev)¶
- update β tries to evolve schema without dropping data (dev-friendly).
- create-drop β creates fresh schema on start, drops on shutdown (tests only).
- create β drop + create on each startup.
- validate β just checks schema matches entities; fails if not.
B) SQL scripts (deterministic, app-driven)¶
Put files in src/main/resources/:
schema.sqlβ runs firstdata.sqlβ runs after schema
Example schema.sql:
Example data.sql:
Enable init:
C) Database migrations (best for real projects)¶
Use Flyway or Liquibase for versioned migrations.
Flyway example (recommended):
spring.jpa.hibernate.ddl-auto=none
spring.flyway.enabled=true
spring.flyway.locations=classpath:db/migration
Place migrations in:
4. Using DB users¶
- Use
appuser/apppass(created by Docker setup) inapplication.properties. - Donβt use
rootfor apps; keep root only for DB admin. - If you add new users (e.g.,
reporter), just switch the credentials.
5. Testing the connection¶
- Run
docker compose psβ MySQL container must show as running. - In Spring Boot logs you should see:
β With this config:
- Spring Boot talks to the MySQL container via
localhost:3306. - You can decide whether JPA builds schema, you run SQL scripts, or Flyway manages migrations.
- Everything stays local, consistent, and reproducible.
Spring Boot Γ MySQL schema strategy cheatsheet¶
TL;DR recommendation¶
- Dev (local): JPA
updateor Flyway - Test (integration):
create-drop(throwaway DB) or Flyway clean+migrate - Prod: Flyway/Liquibase (versioned migrations). Avoid auto-DDL.
Option A β JPA auto-DDL¶
What: Hibernate creates/changes tables from your entities.
Use when
- Fast local prototyping.
- Early-stage projects.
Pros
- Zero SQL to write.
- Very quick iteration.
Cons
- Not deterministic across environments.
- Risky in prod (unexpected alters/drops, subtle diffs).
Key settings
# application-dev.properties
spring.jpa.hibernate.ddl-auto=update # dev-friendly
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect
Guardrails
- Never use
create/create-dropin prod. - Switch to
validatebefore deploying.
Option B β Spring SQL scripts (schema.sql, data.sql)¶
What: Spring runs schema.sql then data.sql at startup.
Use when
- You want deterministic startup without a migration tool.
- Simple apps or demos.
Pros
- Clear order: schema β data.
- Easy to reason about.
Cons
- No versioning/history out of the box.
- Harder to evolve schema over time.
Key settings
# disable Hibernate DDL; let SQL files run
spring.jpa.hibernate.ddl-auto=none
spring.sql.init.mode=always # or "embedded" (defaults for H2)
spring.sql.init.encoding=UTF-8
File layout
Tip
- Make inserts idempotent when possible (
ON DUPLICATE KEY UPDATE) so app restarts donβt explode.
Option C β Migrations (Flyway / Liquibase) β ¶
What: Versioned, incremental SQL (or YAML/XML) migrations, applied automatically.
Use when
- Teamwork, CI/CD, multiple environments.
- Long-lived apps with auditable schema changes.
Pros
- Deterministic & repeatable across envs.
- Easy rollback strategies (by version).
- Plays great with CI/CD.
Cons
- Slightly more setup.
- Requires discipline (one migration per change).
Flyway example
spring.jpa.hibernate.ddl-auto=none
spring.flyway.enabled=true
spring.flyway.locations=classpath:db/migration
# Optional but handy:
# spring.flyway.clean-disabled=true # protect prod
# spring.flyway.baseline-on-migrate=true # adopt existing DBs
src/main/resources/db/migration/
ββ V1__init.sql
ββ V2__add_orders.sql
ββ V3__seed_lookup_data.sql
Liquibase (alt)
spring.jpa.hibernate.ddl-auto=none
spring.liquibase.change-log=classpath:db/changelog/db.changelog-master.yaml
Which to pick by environment¶
| Environment | Recommended | Why |
|---|---|---|
| Dev (local) | JPA update or Flyway |
Speed vs. discipline. Flyway mirrors prod behavior. |
| Test (integration) | create-drop or Flyway clean+migrate |
Fresh DB per run; predictable tests. |
| Prod | Flyway/Liquibase + ddl-auto=validate |
Versioned, controlled changes; fail fast on mismatches. |
Typical profile files (quick copy)¶
application-dev.properties
spring.datasource.url=jdbc:mysql://localhost:3306/appdb
spring.datasource.username=appuser
spring.datasource.password=apppass
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect
application-test.properties
spring.datasource.url=jdbc:mysql://localhost:3306/appdb
spring.datasource.username=appuser
spring.datasource.password=apppass
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect
application-prod.properties
spring.datasource.url=jdbc:mysql://localhost:3306/appdb
spring.datasource.username=${DB_USER}
spring.datasource.password=${DB_PASS}
spring.jpa.hibernate.ddl-auto=validate
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect
spring.flyway.enabled=true
spring.flyway.locations=classpath:db/migration
Common workflows¶
Starting from scratch (solo dev)¶
- Begin with JPA
update. - When entities stabilize, generate SQL (from DB or JPA tools) and move to Flyway V1.
- Set
ddl-auto=validate, build new features with V2+, V3+ migrations.
Team/CI from day 1¶
- Start with Flyway.
- Every schema change = new
Vx__description.sql. - CI runs app β Flyway migrates β integration tests run.
Troubleshooting tips¶
- Connection refused? Ensure Docker MySQL is up and mapped (
3306:3306), and your URL matches the port. - Timezone warnings? Add
serverTimezone=UTConly if you use older drivers; with modern MySQL 8 +TZset in the container, you typically donβt need it. - Access denied? Use app-level user (not root). Confirm grants and password.
- Migrations failing in prod? Enable
baseline-on-migrateto adopt an existing DB; disablecleanin prod.
Quick decision helper¶
- Need speed today? β JPA
update. - Need consistency tomorrow? β Flyway/Liquibase.
- Need repeatable seeds for demos? β
schema.sql+data.sqlor Flyway with aV2__seed.sql.