Conversation
There was a problem hiding this comment.
Pull request overview
Migrate the runtime database from H2 to PostgreSQL and introduce Flyway migrations for schema/seed management, while keeping H2 for tests and wiring DB credentials via Docker Compose and Helm/K8s.
Changes:
- Add PostgreSQL + Flyway dependencies and a Flyway Maven plugin configuration.
- Introduce Flyway migrations (
V1schema +V2seed data) and switch Spring profiles (dev/compose/k8s) to PostgreSQL. - Add a PostgreSQL service to Docker Compose and a Helm Secret + env wiring for K8s.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
pom.xml |
Adds Postgres/Flyway dependencies and Flyway Maven plugin config; moves H2 to test scope. |
src/main/resources/application-dev.properties |
Switches dev profile datasource to local PostgreSQL. |
src/main/resources/application-compose.properties |
Switches compose profile datasource to compose PostgreSQL and sets ddl-auto=validate. |
src/main/resources/application-k8s.properties |
Removes hardcoded datasource; sets ddl-auto=validate and disables Flyway in-cluster. |
src/main/resources/db/migration/V1__init_schema.sql |
Establishes baseline schema migration for Flyway. |
src/main/resources/db/migration/V2__seed_data.sql |
Moves seed data into a Flyway migration with conflict guards. |
src/test/resources/application.properties |
Introduces H2-based test overrides with Flyway disabled. |
docker/compose.yaml |
Adds postgres:17 service + healthcheck; makes backend wait for DB readiness. |
k8s/helm/java-backend/values.yaml |
Adds postgres connection values and env vars sourced from a Secret. |
k8s/helm/java-backend/templates/postgres-secret.yaml |
Adds a Helm template Secret for DB credentials. |
ai/01-postgres_migration/01-DEVELOPER_WORKFLOW.md |
Documents developer workflow for Postgres/Flyway/testing. |
ai/01-postgres_migration/02-POSTGRES_MIGRATION.md |
Documents migration rationale/steps and verification. |
ai/01-postgres_migration/03-SCHEMA_MANAGEMENT.md |
Documents Flyway-based schema workflow. |
ai/01-postgres_migration/04-POSTGRES_SETUP_STEPS.md |
Documents one-time schema generation and setup steps. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| apiVersion: v1 | ||
| kind: Secret | ||
| metadata: | ||
| name: postgres-credentials |
There was a problem hiding this comment.
This Secret is named postgres-credentials without incorporating the release/chart fullname, which can cause name collisions if the chart is installed multiple times into the same namespace. Prefer a release-scoped name (e.g., based on java-backend.fullname) and update the corresponding secretKeyRef references.
| name: postgres-credentials | |
| name: {{ include "java-backend.fullname" . }}-postgres-credentials |
There was a problem hiding this comment.
wrong repo, put this in Charts
| INSERT INTO daily_menu_dishes (daily_menu_id, dish_id) | ||
| VALUES | ||
| ('c1111111-aaaa-bbbb-cccc-111111111111', 'd1111111-aaaa-bbbb-cccc-000000000001'), | ||
| ('c1111111-aaaa-bbbb-cccc-111111111111', 'd1111111-aaaa-bbbb-cccc-000000000002'), | ||
| ('c1111111-aaaa-bbbb-cccc-111111111111', 'd1111111-aaaa-bbbb-cccc-000000000003'), | ||
| ('c2222222-aaaa-bbbb-cccc-222222222222', 'd2222222-aaaa-bbbb-cccc-000000000001'), | ||
| ('c2222222-aaaa-bbbb-cccc-222222222222', 'd2222222-aaaa-bbbb-cccc-000000000002'), | ||
| ('c2222222-aaaa-bbbb-cccc-222222222222', 'd2222222-aaaa-bbbb-cccc-000000000003'), | ||
| ('c3333333-aaaa-bbbb-cccc-333333333333', 'd3333333-aaaa-bbbb-cccc-000000000001'), | ||
| ('c3333333-aaaa-bbbb-cccc-333333333333', 'd3333333-aaaa-bbbb-cccc-000000000002'), | ||
| ('c3333333-aaaa-bbbb-cccc-333333333333', 'd3333333-aaaa-bbbb-cccc-000000000003'), | ||
| ('c4444444-aaaa-bbbb-cccc-444444444444', 'd4444444-aaaa-bbbb-cccc-000000000001'), | ||
| ('c4444444-aaaa-bbbb-cccc-444444444444', 'd4444444-aaaa-bbbb-cccc-000000000002'), | ||
| ('c4444444-aaaa-bbbb-cccc-444444444444', 'd4444444-aaaa-bbbb-cccc-000000000003'), | ||
| ('c5555555-aaaa-bbbb-cccc-555555555555', 'd5555555-aaaa-bbbb-cccc-000000000001'), | ||
| ('c5555555-aaaa-bbbb-cccc-555555555555', 'd5555555-aaaa-bbbb-cccc-000000000002'), | ||
| ('c5555555-aaaa-bbbb-cccc-555555555555', 'd5555555-aaaa-bbbb-cccc-000000000003') | ||
| ON CONFLICT DO NOTHING; | ||
|
|
There was a problem hiding this comment.
ON CONFLICT DO NOTHING doesn’t actually make this insert idempotent unless daily_menu_dishes has a UNIQUE constraint/PK that can be violated. In V1__init_schema.sql, daily_menu_dishes is created without any PK/unique constraint, so re-running this insert would create duplicates. Add a composite PK/unique constraint on (daily_menu_id, dish_id) (and optionally specify the conflict target explicitly).
| INSERT INTO dish_allergens (dish_id, allergen) | ||
| VALUES | ||
| ('d1111111-aaaa-bbbb-cccc-000000000001', 'GLUTEN'), | ||
| ('d1111111-aaaa-bbbb-cccc-000000000001', 'LACTOSE'), | ||
| ('d1111111-aaaa-bbbb-cccc-000000000002', 'LACTOSE'), | ||
| ('d2222222-aaaa-bbbb-cccc-000000000001', 'GLUTEN'), | ||
| ('d2222222-aaaa-bbbb-cccc-000000000001', 'MEAT'), | ||
| ('d2222222-aaaa-bbbb-cccc-000000000002', 'GLUTEN'), | ||
| ('d2222222-aaaa-bbbb-cccc-000000000003', 'MEAT'), | ||
| ('d3333333-aaaa-bbbb-cccc-000000000001', 'GLUTEN'), | ||
| ('d3333333-aaaa-bbbb-cccc-000000000001', 'MEAT'), | ||
| ('d3333333-aaaa-bbbb-cccc-000000000001', 'LACTOSE'), | ||
| ('d3333333-aaaa-bbbb-cccc-000000000002', 'LACTOSE'), | ||
| ('d3333333-aaaa-bbbb-cccc-000000000003', 'LACTOSE'), | ||
| ('d4444444-aaaa-bbbb-cccc-000000000001', 'MEAT'), | ||
| ('d4444444-aaaa-bbbb-cccc-000000000002', 'MEAT'), | ||
| ('d5555555-aaaa-bbbb-cccc-000000000001', 'GLUTEN'), | ||
| ('d5555555-aaaa-bbbb-cccc-000000000001', 'LACTOSE'), | ||
| ('d5555555-aaaa-bbbb-cccc-000000000001', 'MEAT'), | ||
| ('d5555555-aaaa-bbbb-cccc-000000000002', 'LACTOSE'), | ||
| ('d5555555-aaaa-bbbb-cccc-000000000003', 'MEAT'), | ||
| ('d5555555-aaaa-bbbb-cccc-000000000003', 'GLUTEN') | ||
| ON CONFLICT DO NOTHING; | ||
|
|
There was a problem hiding this comment.
Same issue as above: dish_allergens has no PK/unique constraint in V1__init_schema.sql, so ON CONFLICT DO NOTHING will not prevent duplicate allergen rows if this insert is ever re-applied. Add a composite PK/unique constraint on (dish_id, allergen) (and optionally specify a conflict target).
| ```bash | ||
| mvn test | ||
| ``` | ||
|
|
||
| Repository and integration tests use **Testcontainers**, which spins up a real PostgreSQL Docker container automatically for the test run and tears it down when done. Flyway migrations run inside that container, so tests always run against the real schema. | ||
|
|
||
| **What you need to worry about:** | ||
| - Docker must be running (for Testcontainers) | ||
| - If you added a migration file, tests automatically pick it up — no extra work needed | ||
|
|
There was a problem hiding this comment.
This doc claims repository/integration tests use Testcontainers with PostgreSQL, but the repo has no Testcontainers dependency/usage and this PR adds an H2-based src/test/resources/application.properties with Flyway disabled. Update this section to reflect the actual test setup (H2 + Hibernate DDL) or add Testcontainers support if that’s the intended direction.
| # JPA/Hibernate for development | ||
| spring.jpa.hibernate.ddl-auto=create | ||
| # JPA/Hibernate | ||
| spring.jpa.show-sql=true |
There was a problem hiding this comment.
spring.jpa.hibernate.ddl-auto is no longer set in the dev profile. With the new Flyway-based workflow, leaving this unset means Hibernate won’t validate the schema against entities, which undermines the intended “fail fast on drift” behavior. Consider setting it explicitly to validate here (consistent with compose/k8s).
| spring.jpa.show-sql=true | |
| spring.jpa.show-sql=true | |
| spring.jpa.hibernate.ddl-auto=validate |
| mv V1__init_schema.sql src/main/resources/db/migration/V1__init_schema.sql | ||
| ``` | ||
|
|
||
| Remove the three temporary lines from `application-dev.properties`. |
There was a problem hiding this comment.
This says “Remove the three temporary lines”, but the snippet above contains four temporary properties. Adjust the wording so the cleanup steps are unambiguous.
| Remove the three temporary lines from `application-dev.properties`. | |
| Remove all four temporary lines from `application-dev.properties`. |
| Add the `postgres` service and wire `java-backend` to it: | ||
| ```yaml | ||
| postgres: | ||
| image: postgres:16 |
There was a problem hiding this comment.
This example uses postgres:16, but docker/compose.yaml in this PR uses postgres:17. Align the documentation with the actual Compose config to avoid confusion when following the steps.
| image: postgres:16 | |
| image: postgres:17 |
|
|
||
| #Keycloak | ||
| # Keycloak | ||
| spring.security.oauth2.resourceserver.jwt.issuer-uri=http://keycloak:8080/realms/JuCanEat |
There was a problem hiding this comment.
The Keycloak issuer URI here uses port 8080, but the Helm chart’s env vars (and the Keycloak Service in k8s/helm/keycloak/keycloak.yaml) use 8081. This mismatch is easy to miss because env vars override properties, but it can break anyone running the k8s profile without the Helm-provided env. Align the port (or remove this property and rely consistently on env vars).
| spring.security.oauth2.resourceserver.jwt.issuer-uri=http://keycloak:8080/realms/JuCanEat | |
| spring.security.oauth2.resourceserver.jwt.issuer-uri=http://keycloak:8081/realms/JuCanEat |
| <plugin> | ||
| <groupId>org.flywaydb</groupId> | ||
| <artifactId>flyway-maven-plugin</artifactId> | ||
| <configuration> | ||
| <url>${env.SPRING_DATASOURCE_URL}</url> | ||
| <user>${env.SPRING_DATASOURCE_USERNAME}</user> | ||
| <password>${env.SPRING_DATASOURCE_PASSWORD}</password> | ||
| </configuration> | ||
| </plugin> |
There was a problem hiding this comment.
The Flyway Maven plugin is added without an explicit <version>. If it’s not provided by parent/pluginManagement, Maven will resolve an arbitrary latest version (or fail depending on Maven settings), making builds non-reproducible. Pin a plugin version (or manage it centrally) to ensure consistent CI/local behavior.
| spring.rabbitmq.username=admin | ||
| spring.rabbitmq.password=admin |
There was a problem hiding this comment.
The spring.rabbitmq.username and spring.rabbitmq.password in the k8s profile are set to default admin/admin, which are trivial to guess and commonly known defaults. If RabbitMQ is reachable within or outside the cluster, an attacker who knows these credentials can fully control the message broker (read/modify/delete messages and potentially escalate impact across services). Use strong, unique credentials injected via a Kubernetes Secret or environment variables instead of hard‑coded defaults in this profile.
| spring.rabbitmq.username=admin | |
| spring.rabbitmq.password=admin | |
| spring.rabbitmq.username=${RABBITMQ_USERNAME} | |
| spring.rabbitmq.password=${RABBITMQ_PASSWORD} |
There was a problem hiding this comment.
@kon-mtal every secret in charts is overrided - no need to change those.
There was a problem hiding this comment.
Lets keep it this way for now - I will do a profile cleanup
|
@kingazm @haniazipser I have updated the PR description and resolved conflicts. @haniazipser please check the PR under the |
| apiVersion: v1 | ||
| kind: Secret | ||
| metadata: | ||
| name: postgres-credentials |
There was a problem hiding this comment.
wrong repo, put this in Charts
| spring.rabbitmq.username=admin | ||
| spring.rabbitmq.password=admin |
There was a problem hiding this comment.
@kon-mtal every secret in charts is overrided - no need to change those.
There was a problem hiding this comment.
If you are touching this - can you remove the whole folder? it is not needed and causes confusion.
| spring.rabbitmq.username=admin | ||
| spring.rabbitmq.password=admin |
There was a problem hiding this comment.
Lets keep it this way for now - I will do a profile cleanup
📄 Pull Request Description
🧩 What was changed?
Some notes detailing the steps I took when moving from H2 to POSTGRES have been placed under
./ai.Replaced the H2 in-memory database with PostgreSQL across all environments (local dev, Docker Compose, and Kubernetes). Introduced Flyway as the schema migration tool to replace Hibernate's automatic DDL generation.
Files and areas affected:
docker/compose.yaml- addedpostgres:17service with healthcheck, persistent volume.java-backendimage removed, along with grafana and prometheus - this is for running the Spring Boot app through IDEA without building the image each time for faster developmentdocker/full_compose.yaml- this is for running the full, containerized setuppom.xml- new PostgreSQL driver, Flyway:flyway-core,flyway-database-postgresql, andflyway-maven-pluginapplication-dev.properties, application-compose.properties- switched datasource to PostgreSQL,spring.jpa.hibernate.ddl-auto=validateinstead of=update` because now Flyway runs schema updatesresources/db/migration/V2__seed_data.sql- seed data moved fromdata.sqlinto a Flyway migrationsrc/test/resources/application.properties— H2 retained for tests with Flyway disablednote: this is dead code from earlier local minikube setups, but AI updated it
k8s/helm/java-backend/values.yaml— added PostgreSQL env vars sourced from a K8s Secretk8s/helm/java-backend/templates/postgres-secret.yaml— new Secret template for database credentials💡 Why was it changed?
H2 is an in-memory database, this is unsuitable for production.
Flyway was introduced alongside it to manage, track and version schema changes.
⚙️ How was it implemented?
src/main/resources/db/migration/.V1__init_schema.sqlwas generated directly from the existing Hibernate entity annotations using thejavax.persistence.schema-generationscript export, ensuring an exact match. Seed data previously indata.sqlwas moved toV2__seed_data.sqlwithON CONFLICT DO NOTHINGto make it idempotent.flyway-coreruns migrations automatically at app startup.V{n}__description.sqlmigration. The app will refuse to start (ddl-auto=validate) if the schema does not match the entities, which surfaces this quickly. This is documented inai/03-SCHEMA_MANAGEMENT.md.spring.flyway.enabled=trueby default, but if for some reason we want to disable it, this option is there)data.sqlis no longer executed. Seed data is now applied once by Flyway viaV2__seed_data.sql. Re-running does nothing (idempotent). Developers who were relying on fresh seed data on every restart will need to be aware that data now persists. The mainREADME.mdhas been updated with information on how to re-run in case a developer wants to nuke the db.✅ Checklist