Tài liệu này tổng hợp các thiết lập hữu ích và mẹo khi sử dụng R2DBC kết hợp với PostgreSQL trong Spring Boot.

Thiết lập Dependencies (Gradle)

plugins {
    id("org.springframework.boot") version "3.2.5"
    id("io.spring.dependency-management") version "1.1.4"
    kotlin("jvm") version "1.9.23"
}

// ...

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-data-r2dbc")
    implementation("io.r2dbc:r2dbc-postgresql")
    // runtimeOnly("org.postgresql:postgresql") // Cần nếu dùng Flyway hoặc công cụ JDBC
}

Cấu hình application.yml mẫu

spring:
  r2dbc:
    url: r2dbc:postgresql://localhost:5432/dummy
    username: devuser
    password: devpass
    pool:
      enabled: true
      initial-size: 5
      max-size: 20
      max-idle-time: 30s
      max-create-connection-time: 5s
      validation-query: SELECT 1
    properties:
      connectTimeout: PT15S
      ssl: false
      maxCreateConnectionTime: PT3S
      maxAcquireTime: PT10S
      maxLifeTime: PT300S

Ví dụ sử dụng R2dbcEntityTemplate

Criteria API

val criteria = Criteria.where("name").like("%test%")
template.select<DummyEntity>()
    .matching(Query.query(criteria))
    .all()
    .collectList()

Truy vấn phức hợp

val query = Query
    .query(Criteria.where("name").like("%test%"))
    .limit(10)
    .sort(Sort.by("id").descending())

template.select<DummyEntity>()
    .matching(query)
    .all()

Binding tham số trực tiếp với DatabaseClient

template.databaseClient
    .sql("SELECT * FROM dummy WHERE name like %:name%")
    .bind("name", "test")
    .map { row -> row.get("name", String::class.java) }
    .first()

Kích hoạt Log truy vấn

logging:
  level:
    org.springframework.r2dbc.core: DEBUG
    io.r2dbc.spi: DEBUG # Bỏ ghi chú nếu muốn xem query và query-param
    io.r2dbc.postgresql.QUERY: DEBUG
    io.r2dbc.postgresql.PARAM: DEBUG

Testcontainers + Liên kết SQL khởi tạo PostgreSQL

object TestPostgresContainer {
    @Container
    val container = PostgreSQLContainer("postgres:15").apply {
        withDatabaseName("dummy_db")
        withUsername("devuser")
        withPassword("devpass")
        withInitScript("sql/init.sql") // đường dẫn: src/test/resources/sql/init.sql
        start()
    }
}
class TestPostgresInitializer : ApplicationContextInitializer<ConfigurableApplicationContext> {
    override fun initialize(context: ConfigurableApplicationContext) {
        val c = TestPostgresContainer.container
        val props = mapOf(
            "spring.r2dbc.url" to "r2dbc:postgresql://:/",
            "spring.r2dbc.username" to c.username,
            "spring.r2dbc.password" to c.password,
            "spring.datasource.url" to c.jdbcUrl,
            "spring.datasource.username" to c.username,
            "spring.datasource.password" to c.password
        )
        context.environment.propertySources.addFirst(MapPropertySource("testcontainers", props))
    }
}

Thiết lập Docker Compose cho PostgreSQL & pgAdmin

file docker-compose.yml

version: '3.8'

services:
  postgres:
    image: postgres:15
    container_name: r2dbc_postgres
    restart: unless-stopped
    ports:
      - "25432:5432"
    environment:
      POSTGRES_USER: devuser
      POSTGRES_PASSWORD: devpass
      POSTGRES_DB: dummy
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./initdb:/docker-entrypoint-initdb.d

  pgadmin:
    image: dpage/pgadmin4
    container_name: pgadmin
    restart: unless-stopped
    ports:
      - "38080:80"
    environment:
      PGADMIN_DEFAULT_EMAIL: admin@local.com
      PGADMIN_DEFAULT_PASSWORD: admin123
      PGADMIN_CONFIG_SERVER_MODE: 'False'
    volumes:
      - pgadmin_data:/var/lib/pgadmin
      - ./pgadmin/servers.json:/pgadmin4/servers.json
      - ./pgadmin/.pgpass:/pgadmin4/.pgpass
    depends_on:
      - postgres

volumes:
  postgres_data:
  pgadmin_data:

file .pgpass (Đặt trong thư mục pgadmin)

postgres:5432:dummy:devuser:devpass

file servers.json (Đặt trong thư mục pgadmin)

{
 "Servers": {
  "1": {
  "Name": "dummy-db",
  "Group": "Servers",
  "Host": "postgres",
  "Port": 5432,
  "MaintenanceDB": "dummy",
  "Username": "devuser",
  "SSLMode": "prefer",
  "PassFile": ".pgpass"
  }
 }
}

Các lưu ý khác

Phiên bản r2dbc-postgresql thường được quản lý tự động bởi Spring Boot BOM. Nếu không tự động tải được, bạn có thể chỉ định phiên bản cụ thể từ Maven Central.
Nguồn bài viết ryukato.github.io