Switch database from H2 to PostgreSQL - #43
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughThis PR migrates the application from H2 in-memory database to PostgreSQL with persistent storage, introduces Flyway for database migrations, updates development workflows to use Docker Compose for local database management, and adjusts application configuration to support environment-specific setup with local-only credentials. Changes
Sequence DiagramsequenceDiagram
actor Dev as Developer
participant IDE as IntelliJ IDE
participant Script as start-db.sh
participant Docker as Docker Compose
participant Postgres as PostgreSQL Service
participant App as Spring Boot App
participant Flyway as Flyway
Dev->>IDE: Click "Spring Development" Run Config
IDE->>IDE: Execute pre-run task
IDE->>Script: Trigger Docker Compose Up + Wait
Script->>Docker: docker compose up -d
Docker->>Postgres: Start postgres:17 container
Postgres->>Postgres: Initialize database
Script->>Postgres: Loop: pg_isready check
Postgres-->>Script: Ready (healthcheck passes)
Script->>IDE: Return success
IDE->>App: Launch Spring Boot (--spring.profiles.active=local)
App->>Flyway: Initialize Flyway
Flyway->>Postgres: Apply V1__init.sql (create contact table)
Flyway->>Postgres: Apply V2__seed.sql (insert sample data)
Postgres-->>Flyway: Migrations complete
App->>App: Boot complete
App-->>Dev: Ready for development
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 3❌ Failed checks (3 warnings)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
- Remove hardcoded security credentials from `application-dev.yaml`. - Add `application-local.yaml` to `.gitignore`. - Activate `local` profile in the Spring Development run configuration.
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (2)
.run/Spring Development.run.xml (1)
3-8: LGTM for profile and Docker integration.The switch from
devtolocalprofile aligns with the PostgreSQL setup, and the Docker Compose pre-run task ensures the database is available.However, note that
SecurityConfig.javastill contains H2 console permit rules (/h2-console/**) which are now dead code since H2 is only used for tests.Consider removing the H2 console security rules from
SecurityConfig.javain a follow-up:// These rules can be removed as H2 console is no longer available at runtime: .requestMatchers("/h2-console/**").permitAll() .csrf(csrf -> csrf.ignoringRequestMatchers("/h2-console/**"))🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.run/Spring Development.run.xml around lines 3 - 8, Remove the dead H2-console related security rules from SecurityConfig.java: delete the .requestMatchers("/h2-console/**").permitAll() entry and the .csrf(csrf -> csrf.ignoringRequestMatchers("/h2-console/**")) configuration (or the portions that reference "/h2-console/**") so the security configuration no longer includes H2 console permit/CSRF exceptions now that H2 is test-only.docker-compose.yml (1)
4-11: Add a DB healthcheck to reduce startup race conditions.Compose starts the container before PostgreSQL is fully ready. A healthcheck improves local reliability (especially with automated startup flows).
Suggested change
db: image: postgres:17 environment: POSTGRES_DB: contacts POSTGRES_USER: contacts POSTGRES_PASSWORD: contacts + healthcheck: + test: ["CMD-SHELL", "pg_isready -U contacts -d contacts"] + interval: 5s + timeout: 5s + retries: 12 ports: - "5432:5432"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docker-compose.yml` around lines 4 - 11, Add a healthcheck stanza to the PostgreSQL service in docker-compose.yml to avoid startup race conditions: under the service definition that currently has environment/ports/volumes, add a healthcheck that runs pg_isready (or psql -c '\q') against POSTGRES_USER/POSTGRES_DB, set sensible interval (e.g., 10s), timeout (e.g., 5s) and retries (e.g., 5) and use CMD-SHELL so the container reports healthy only when the DB is accepting connections; ensure the healthcheck uses the same port 5432 and exits non-zero on failure so Docker Compose can wait for healthy status.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@pom.xml`:
- Around line 37-48: In the pom.xml Flyway dependencies block, replace the
non-existent artifactId "spring-boot-flyway" with the correct Spring Boot
starter "spring-boot-starter-flyway" (groupId org.springframework.boot,
artifactId currently listed as spring-boot-flyway) so Flyway auto-configuration
and dependency management work under Spring Boot 4; leave the other Flyway
entries (org.flywaydb:flyway-core and org.flywaydb:flyway-database-postgresql)
unchanged.
In `@README.md`:
- Around line 73-83: Replace the bare "docker compose up -d" step with the
readiness-aware script invocation to avoid race conditions: update the README
step that currently shows `docker compose up -d` to use `./scripts/start-db.sh`
so the DB readiness check runs before the next step (`./mvnw spring-boot:run
-Dspring-boot.run.profiles=local`); ensure the surrounding text/instructions
still reference the same order and mention the script by name
(`scripts/start-db.sh`) so users run the readiness wrapper instead of launching
Compose directly.
In `@scripts/start-db.sh`:
- Around line 1-5: The startup loop in the script that runs "docker compose exec
-T db pg_isready -q" can hang forever; add a max retry/count or timeout variable
(e.g., MAX_RETRIES or TIMEOUT_SECONDS) and a counter (RETRY_COUNT) in the loop
that breaks and exits non‑zero with an error log when exceeded, so the until
loop stops after the limit and the script fails fast if "pg_isready" never
returns healthy.
In `@src/main/resources/application.yaml`:
- Around line 10-12: The datasource URL property in application.yaml is using
the env var DB_URL while deployment expects DATABASE_URL; update the url mapping
to read ${DATABASE_URL} instead of ${DB_URL} so the datasource configuration
(the url property) aligns with the deployment/migration contract and the app can
start when only DATABASE_URL is supplied.
---
Nitpick comments:
In @.run/Spring Development.run.xml:
- Around line 3-8: Remove the dead H2-console related security rules from
SecurityConfig.java: delete the .requestMatchers("/h2-console/**").permitAll()
entry and the .csrf(csrf -> csrf.ignoringRequestMatchers("/h2-console/**"))
configuration (or the portions that reference "/h2-console/**") so the security
configuration no longer includes H2 console permit/CSRF exceptions now that H2
is test-only.
In `@docker-compose.yml`:
- Around line 4-11: Add a healthcheck stanza to the PostgreSQL service in
docker-compose.yml to avoid startup race conditions: under the service
definition that currently has environment/ports/volumes, add a healthcheck that
runs pg_isready (or psql -c '\q') against POSTGRES_USER/POSTGRES_DB, set
sensible interval (e.g., 10s), timeout (e.g., 5s) and retries (e.g., 5) and use
CMD-SHELL so the container reports healthy only when the DB is accepting
connections; ensure the healthcheck uses the same port 5432 and exits non-zero
on failure so Docker Compose can wait for healthy status.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 0738a5ae-bf68-42e3-bccb-a3be636b8748
📒 Files selected for processing (13)
.gitignore.idea/dictionaries/project.xml.run/Docker Compose Up.run.xml.run/Spring Development.run.xmlREADME.mddocker-compose.ymlpom.xmlscripts/start-db.shsrc/main/resources/application-dev.yamlsrc/main/resources/application.yamlsrc/main/resources/db/migration/V1__init.sqlsrc/main/resources/db/migration/V2__seed.sqlsrc/test/resources/application.yaml
💤 Files with no reviewable changes (1)
- src/main/resources/application-dev.yaml
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/main/resources/db/seed/V2__seed.sql`:
- Around line 1-31: The Flyway seed file V2__seed.sql is placed under db/seed
but your app's Flyway config (spring.flyway.locations in application.yaml)
defaults to classpath:db/migration so the seed never runs; fix by either moving
V2__seed.sql into db/migration (e.g.,
src/main/resources/db/migration/V2__seed.sql) or updating application.yaml to
set spring.flyway.locations to include both classpath:db/migration and
classpath:db/seed so Flyway will scan the seed directory.
- Around line 1-31: Move the seed migration file V2__seed.sql out of
src/main/resources/db/seed/ into the Flyway-managed directory db/migration/ so
Flyway will pick it up (remove the old copy in db/seed/ to avoid confusion),
then run migrations (or a local prod-like startup) to confirm the data is seeded
by Flyway and not duplicated by any other mechanism; specifically check for any
data.sql file and verify sql.init.mode remains at its production default so
data.sql is not executed, and update project notes/README to document that
Flyway (db/migration/) is the single canonical seeding path.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 4a954b64-86e9-4d44-acc4-0005a5c9aaa9
📒 Files selected for processing (3)
src/main/resources/application.yamlsrc/main/resources/db/seed/V2__seed.sqlsrc/test/resources/application.yaml
✅ Files skipped from review due to trivial changes (2)
- src/test/resources/application.yaml
- src/main/resources/application.yaml
| INSERT INTO contact (id, slug, first, last, email, phone) VALUES | ||
| ('550e8400-e29b-41d4-a716-446655440000', 'john-doe', 'John', 'Doe', 'john@example.com', '555-1234'), | ||
| ('550e8400-e29b-41d4-a716-446655440001', 'jane-doe', 'Jane', 'Doe', 'jane@example.com', '555-5678'), | ||
| ('550e8400-e29b-41d4-a716-446655440002', 'alice-smith', 'Alice', 'Smith', 'alice.smith@example.com', '555-1001'), | ||
| ('550e8400-e29b-41d4-a716-446655440003', 'bob-johnson', 'Bob', 'Johnson', 'bob.johnson@example.com', '555-1002'), | ||
| ('550e8400-e29b-41d4-a716-446655440004', 'carol-williams', 'Carol', 'Williams', 'carol.williams@example.com', '555-1003'), | ||
| ('550e8400-e29b-41d4-a716-446655440005', 'david-brown', 'David', 'Brown', 'david.brown@example.com', '555-1004'), | ||
| ('550e8400-e29b-41d4-a716-446655440006', 'eve-jones', 'Eve', 'Jones', 'eve.jones@example.com', '555-1005'), | ||
| ('550e8400-e29b-41d4-a716-446655440007', 'frank-garcia', 'Frank', 'Garcia', 'frank.garcia@example.com', '555-1006'), | ||
| ('550e8400-e29b-41d4-a716-446655440008', 'grace-miller', 'Grace', 'Miller', 'grace.miller@example.com', '555-1007'), | ||
| ('550e8400-e29b-41d4-a716-446655440009', 'henry-davis', 'Henry', 'Davis', 'henry.davis@example.com', '555-1008'), | ||
| ('550e8400-e29b-41d4-a716-44665544000a', 'iris-martinez', 'Iris', 'Martinez', 'iris.martinez@example.com', '555-1009'), | ||
| ('550e8400-e29b-41d4-a716-44665544000b', 'jack-wilson', 'Jack', 'Wilson', 'jack.wilson@example.com', '555-1010'), | ||
| ('550e8400-e29b-41d4-a716-44665544000c', 'karen-anderson', 'Karen', 'Anderson', 'karen.anderson@example.com', '555-1011'), | ||
| ('550e8400-e29b-41d4-a716-44665544000d', 'leo-thomas', 'Leo', 'Thomas', 'leo.thomas@example.com', '555-1012'), | ||
| ('550e8400-e29b-41d4-a716-44665544000e', 'mia-taylor', 'Mia', 'Taylor', 'mia.taylor@example.com', '555-1013'), | ||
| ('550e8400-e29b-41d4-a716-44665544000f', 'noah-hernandez', 'Noah', 'Hernandez', 'noah.hernandez@example.com', '555-1014'), | ||
| ('550e8400-e29b-41d4-a716-446655440010', 'olivia-moore', 'Olivia', 'Moore', 'olivia.moore@example.com', '555-1015'), | ||
| ('550e8400-e29b-41d4-a716-446655440011', 'paul-jackson', 'Paul', 'Jackson', 'paul.jackson@example.com', '555-1016'), | ||
| ('550e8400-e29b-41d4-a716-446655440012', 'quinn-martin', 'Quinn', 'Martin', 'quinn.martin@example.com', '555-1017'), | ||
| ('550e8400-e29b-41d4-a716-446655440013', 'rachel-lee', 'Rachel', 'Lee', 'rachel.lee@example.com', '555-1018'), | ||
| ('550e8400-e29b-41d4-a716-446655440014', 'sam-perez', 'Sam', 'Perez', 'sam.perez@example.com', '555-1019'), | ||
| ('550e8400-e29b-41d4-a716-446655440015', 'tina-white', 'Tina', 'White', 'tina.white@example.com', '555-1020'), | ||
| ('550e8400-e29b-41d4-a716-446655440016', 'ursula-harris', 'Ursula', 'Harris', 'ursula.harris@example.com', '555-1021'), | ||
| ('550e8400-e29b-41d4-a716-446655440017', 'victor-clark', 'Victor', 'Clark', 'victor.clark@example.com', '555-1022'), | ||
| ('550e8400-e29b-41d4-a716-446655440018', 'wendy-lewis', 'Wendy', 'Lewis', 'wendy.lewis@example.com', '555-1023'), | ||
| ('550e8400-e29b-41d4-a716-446655440019', 'xavier-robinson', 'Xavier', 'Robinson', 'xavier.robinson@example.com', '555-1024'), | ||
| ('550e8400-e29b-41d4-a716-44665544001a', 'yara-walker', 'Yara', 'Walker', 'yara.walker@example.com', '555-1025'), | ||
| ('550e8400-e29b-41d4-a716-44665544001b', 'zane-hall', 'Zane', 'Hall', 'zane.hall@example.com', '555-1026'), | ||
| ('550e8400-e29b-41d4-a716-44665544001c', 'amber-allen', 'Amber', 'Allen', 'amber.allen@example.com', '555-1027'), | ||
| ('550e8400-e29b-41d4-a716-44665544001d', 'brian-young', 'Brian', 'Young', 'brian.young@example.com', '555-1028'); |
There was a problem hiding this comment.
Seed migration won’t execute from db/seed with current Flyway config.
With no spring.flyway.locations configured in src/main/resources/application.yaml:1-18, Flyway scans only classpath:db/migration. This file is in classpath:db/seed, so these inserts are skipped and seed data won’t load.
Use one of these fixes:
- Move this file to
src/main/resources/db/migration/V2__seed.sql, or - Configure
spring.flyway.locations: classpath:db/migration,classpath:db/seed.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/main/resources/db/seed/V2__seed.sql` around lines 1 - 31, The Flyway seed
file V2__seed.sql is placed under db/seed but your app's Flyway config
(spring.flyway.locations in application.yaml) defaults to classpath:db/migration
so the seed never runs; fix by either moving V2__seed.sql into db/migration
(e.g., src/main/resources/db/migration/V2__seed.sql) or updating
application.yaml to set spring.flyway.locations to include both
classpath:db/migration and classpath:db/seed so Flyway will scan the seed
directory.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== data.sql (if present) =="
if [ -f src/main/resources/data.sql ]; then
sed -n '1,220p' src/main/resources/data.sql
else
echo "src/main/resources/data.sql not found"
fi
echo
echo "== Flyway/SQL init related config =="
rg -n -C2 'flyway|sql:|init:|defer-datasource-initialization|datasource' \
src/main/resources/application.yaml src/test/resources/application.yaml 2>/dev/null || trueRepository: yamcodes/contacts
Length of output: 4659
🏁 Script executed:
cat -n src/main/resources/application.yamlRepository: yamcodes/contacts
Length of output: 494
🏁 Script executed:
# Check for any other Flyway-related configurations
fd -e yaml -e properties | xargs rg -l 'flyway' 2>/dev/null || trueRepository: yamcodes/contacts
Length of output: 96
🏁 Script executed:
# Check if there's a bootstrap.properties or other config files
fd -e properties -e yml -o -e yaml | head -20Repository: yamcodes/contacts
Length of output: 177
🏁 Script executed:
fd 'application.*\.(yaml|yml|properties)' src/main/resources/ src/test/resources/Repository: yamcodes/contacts
Length of output: 132
🏁 Script executed:
# Check if there's a pom.xml or build.gradle to understand profiles
find . -maxdepth 2 -name 'pom.xml' -o -name 'build.gradle' | xargs ls -la 2>/dev/null || echo "No pom.xml or build.gradle found in root"Repository: yamcodes/contacts
Length of output: 119
🏁 Script executed:
# Check if sql.init.mode is configured anywhere in the codebase
rg 'sql\.init\.mode|sql:' src/ --type yaml --type properties 2>/dev/null || echo "Not found"Repository: yamcodes/contacts
Length of output: 70
🏁 Script executed:
rg -A5 -B2 'flyway' pom.xmlRepository: yamcodes/contacts
Length of output: 668
🏁 Script executed:
# List all SQL migration files
fd -e sql src/main/resources/db/Repository: yamcodes/contacts
Length of output: 454
🏁 Script executed:
# Check if there's a V1 migration
cat -n src/main/resources/db/migration/V1__*.sql 2>/dev/null || echo "No V1 migration found"Repository: yamcodes/contacts
Length of output: 309
🏁 Script executed:
fd . src/main/resources/db/Repository: yamcodes/contacts
Length of output: 206
🏁 Script executed:
# Look for any configuration about Flyway locations
rg 'flyway\.locations|spring\.flyway' src/main/resources/ src/test/resources/Repository: yamcodes/contacts
Length of output: 43
Move V2__seed.sql to standard Flyway location and verify single seeding path after fix.
The seed migration V2__seed.sql is in src/main/resources/db/seed/ but Flyway searches db/migration/ by default. This means in production, neither the Flyway migration nor data.sql will seed the database (sql.init.mode defaults to NEVER for non-H2 databases). Once the migration is moved to db/migration/, confirm that data.sql is not also executed (it won't be, since sql.init.mode is not configured for production, but document this to prevent future confusion).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/main/resources/db/seed/V2__seed.sql` around lines 1 - 31, Move the seed
migration file V2__seed.sql out of src/main/resources/db/seed/ into the
Flyway-managed directory db/migration/ so Flyway will pick it up (remove the old
copy in db/seed/ to avoid confusion), then run migrations (or a local prod-like
startup) to confirm the data is seeded by Flyway and not duplicated by any other
mechanism; specifically check for any data.sql file and verify sql.init.mode
remains at its production default so data.sql is not executed, and update
project notes/README to document that Flyway (db/migration/) is the single
canonical seeding path.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@README.md`:
- Around line 35-71: The README currently instructs creating
application-local.yaml under src/main/resources which risks packaging local
credentials into the JAR; change the instruction to create the file at
./config/application-local.yaml (and adjust the YAML example accordingly) and
update all other README references to application-local.yaml (the later
references that point to src/main/resources/templates/static etc.) to point to
./config/application-local.yaml so local secrets live outside the source tree
and are not copied into build artifacts.
In `@src/main/java/codes/yam/contacts/SecurityConfig.java`:
- Around line 15-18: The method securityFilterChain(HttpSecurity http) must
declare the checked exception thrown by SecurityBuilder.build(); update the
SecurityFilterChain bean method signature (securityFilterChain(HttpSecurity
http)) to add "throws Exception" so the call to http... .build() compiles
against Spring Security 7.x (SecurityBuilder.build()).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 72e84c9b-0e57-4b76-8bd2-47592750c125
📒 Files selected for processing (5)
README.mddocker-compose.ymlscripts/start-db.shsrc/main/java/codes/yam/contacts/SecurityConfig.javasrc/main/resources/application.yaml
✅ Files skipped from review due to trivial changes (2)
- scripts/start-db.sh
- docker-compose.yml
🚧 Files skipped from review as they are similar to previous changes (1)
- src/main/resources/application.yaml
Closes #16
Summary by CodeRabbit
New Features
Chores