Skip to content

Comments

Replace SQLite with PostgreSQL for production, add Testcontainers for tests#348

Open
devin-ai-integration[bot] wants to merge 2 commits intomasterfrom
devin/1770752952-replace-sqlite-with-postgresql
Open

Replace SQLite with PostgreSQL for production, add Testcontainers for tests#348
devin-ai-integration[bot] wants to merge 2 commits intomasterfrom
devin/1770752952-replace-sqlite-with-postgresql

Conversation

@devin-ai-integration
Copy link

@devin-ai-integration devin-ai-integration bot commented Feb 10, 2026

Replace SQLite with PostgreSQL for production, add Testcontainers for tests

Summary

Introduces Spring profiles to support multiple database backends:

  • dev profile (default): SQLite via file-based dev.db — preserves existing local development experience
  • prod profile: PostgreSQL with externalized config via environment variables
  • test profile: Testcontainers spins up a disposable PostgreSQL 14 container per test run

Key changes:

  • Datasource config moved from application.properties into profile-specific files (application-dev.properties, application-prod.properties, application-test.properties)
  • Flyway migrations split into vendor-specific directories (db/migration/sqlite/, db/migration/postgresql/)
  • MyBatis mapper SQL updated for cross-database compatibility: LIMIT x,yLIMIT y OFFSET x, and table alias AT renamed to ATAG (reserved keyword in PostgreSQL)
  • @Sql cleanup added to DbTestBase and ArticleRepositoryTransactionTest for test data isolation across shared Testcontainers instance
  • docker-compose.yml added for running the app with PostgreSQL locally

Review & Testing Checklist for Human

  • The original db/migration/V1__create_tables.sql was NOT deleted. If no profile is active or spring.flyway.locations is unset, Flyway could pick up both the old and new migrations. Verify whether this file should be removed or if the profile-based spring.flyway.locations always takes precedence.
  • PostgreSQL migration has no foreign key constraints — this was intentional to avoid breaking tests that insert rows with fake IDs (e.g., "123", "456"). Decide if this is acceptable for production or if a follow-up V2__add_foreign_keys.sql migration is needed.
  • CI requires Docker for Testcontainers. The GitHub Actions workflow (gradle.yml) was not modified. ubuntu-latest includes Docker, but confirm tests pass in CI and don't time out waiting for container startup.
  • docker-compose.yml references build: . — ensure the existing Dockerfile works with the new SPRING_PROFILES_ACTIVE=prod environment variable.
  • Manually test the dev profile (./gradlew bootRun) to confirm SQLite still works as expected for local development.

Notes


Open with Devin

… tests

- Add PostgreSQL driver and Testcontainers dependencies to build.gradle
- Create Spring profiles: dev (SQLite), prod (PostgreSQL), test (Testcontainers+PostgreSQL)
- Add vendor-specific Flyway migrations for SQLite and PostgreSQL
- Fix MyBatis LIMIT syntax for cross-database compatibility
- Rename reserved keyword alias AT to ATAG in mapper XMLs
- Add SQL cleanup to test base classes for data isolation
- Add docker-compose.yml with PostgreSQL service

Co-Authored-By: Doris Tian <doristian23@gmail.com>
@devin-ai-integration
Copy link
Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

…ories

Co-Authored-By: Doris Tian <doristian23@gmail.com>
Copy link
Author

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 potential issue.

View 4 additional findings in Devin Review.

Open in Devin Review

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 countArticle query still uses reserved keyword AT as table alias, breaking PostgreSQL

The countArticle query in ArticleReadService.xml at lines 68-69 still uses AT as the alias for article_tags. While the PR renamed ATATAG in the selectArticleData and selectArticleIds SQL fragments, the countArticle query has its own inline join that was missed.

Root Cause and Impact

AT is a reserved keyword in PostgreSQL. The other queries in this file were correctly updated to use ATAG (lines 23, 24, 32, 33), but the countArticle query at lines 68-69 was overlooked:

left join article_tags AT on A.id = AT.article_id
left join tags T on T.id = AT.tag_id

This query is used to count the total number of articles matching filter criteria (by tag, author, or favorited-by). When running against PostgreSQL (both prod and test profiles), this query will fail with a syntax error because AT is a reserved keyword.

Impact: The article listing/filtering endpoints that call countArticle (to return total counts for pagination) will throw SQL exceptions on PostgreSQL, making pagination broken in production and tests.

(Refers to lines 68-69)

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants