第17篇-PostgreSQL-Docker环境搭建

【Kotlin + Spring Boot 4 从零到架构师】第 17 篇:PostgreSQL + Docker 环境搭建

本系列定位:零基础入门,从 Kotlin 语法一路到 Spring Boot 4 高级架构(DDD + Modulith),适合 Java 开发者转型,也适合纯新手系统学习。


本篇你将学到

  • 用 Docker 快速部署 PostgreSQL 16
  • Spring Boot 连接 PostgreSQL 的配置
  • HikariCP 连接池的概念与关键参数
  • 验证数据库连通性

学完本篇,mini-shop 将从内存存储升级为真正的数据库持久化。


一、安装 Docker

1.1 为什么用 Docker 部署数据库

  • 一键部署:不需要在系统上手动安装 PostgreSQL
  • 环境隔离:不同项目可以用不同版本的数据库,互不干扰
  • 与生产一致:开发环境与生产环境使用相同的 Docker 镜像
  • 便于清理:不想要了直接删容器,不污染系统

1.2 安装 Docker

Windows / macOS
  1. 访问 Docker Desktop 官网
  2. 下载并安装对应平台的版本
  3. 启动 Docker Desktop,等待右下角图标变为绿色
Linux(Debian/Ubuntu)
bash 复制代码
# 官方一键安装脚本
curl -fsSL https://get.docker.com | sh

# 启动 Docker 服务
sudo systemctl start docker
sudo systemctl enable docker

# 验证
docker --version

验证安装成功:终端执行 docker --version,看到版本号即可。


下面是 Docker 部署 PostgreSQL 的整体流程:
#mermaid-svg-yhs1ep4iMHiOd98x{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-yhs1ep4iMHiOd98x .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-yhs1ep4iMHiOd98x .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-yhs1ep4iMHiOd98x .error-icon{fill:#552222;}#mermaid-svg-yhs1ep4iMHiOd98x .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-yhs1ep4iMHiOd98x .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-yhs1ep4iMHiOd98x .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-yhs1ep4iMHiOd98x .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-yhs1ep4iMHiOd98x .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-yhs1ep4iMHiOd98x .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-yhs1ep4iMHiOd98x .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-yhs1ep4iMHiOd98x .marker{fill:#333333;stroke:#333333;}#mermaid-svg-yhs1ep4iMHiOd98x .marker.cross{stroke:#333333;}#mermaid-svg-yhs1ep4iMHiOd98x svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-yhs1ep4iMHiOd98x p{margin:0;}#mermaid-svg-yhs1ep4iMHiOd98x .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-yhs1ep4iMHiOd98x .cluster-label text{fill:#333;}#mermaid-svg-yhs1ep4iMHiOd98x .cluster-label span{color:#333;}#mermaid-svg-yhs1ep4iMHiOd98x .cluster-label span p{background-color:transparent;}#mermaid-svg-yhs1ep4iMHiOd98x .label text,#mermaid-svg-yhs1ep4iMHiOd98x span{fill:#333;color:#333;}#mermaid-svg-yhs1ep4iMHiOd98x .node rect,#mermaid-svg-yhs1ep4iMHiOd98x .node circle,#mermaid-svg-yhs1ep4iMHiOd98x .node ellipse,#mermaid-svg-yhs1ep4iMHiOd98x .node polygon,#mermaid-svg-yhs1ep4iMHiOd98x .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-yhs1ep4iMHiOd98x .rough-node .label text,#mermaid-svg-yhs1ep4iMHiOd98x .node .label text,#mermaid-svg-yhs1ep4iMHiOd98x .image-shape .label,#mermaid-svg-yhs1ep4iMHiOd98x .icon-shape .label{text-anchor:middle;}#mermaid-svg-yhs1ep4iMHiOd98x .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-yhs1ep4iMHiOd98x .rough-node .label,#mermaid-svg-yhs1ep4iMHiOd98x .node .label,#mermaid-svg-yhs1ep4iMHiOd98x .image-shape .label,#mermaid-svg-yhs1ep4iMHiOd98x .icon-shape .label{text-align:center;}#mermaid-svg-yhs1ep4iMHiOd98x .node.clickable{cursor:pointer;}#mermaid-svg-yhs1ep4iMHiOd98x .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-yhs1ep4iMHiOd98x .arrowheadPath{fill:#333333;}#mermaid-svg-yhs1ep4iMHiOd98x .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-yhs1ep4iMHiOd98x .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-yhs1ep4iMHiOd98x .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-yhs1ep4iMHiOd98x .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-yhs1ep4iMHiOd98x .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-yhs1ep4iMHiOd98x .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-yhs1ep4iMHiOd98x .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-yhs1ep4iMHiOd98x .cluster text{fill:#333;}#mermaid-svg-yhs1ep4iMHiOd98x .cluster span{color:#333;}#mermaid-svg-yhs1ep4iMHiOd98x div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-yhs1ep4iMHiOd98x .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-yhs1ep4iMHiOd98x rect.text{fill:none;stroke-width:0;}#mermaid-svg-yhs1ep4iMHiOd98x .icon-shape,#mermaid-svg-yhs1ep4iMHiOd98x .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-yhs1ep4iMHiOd98x .icon-shape p,#mermaid-svg-yhs1ep4iMHiOd98x .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-yhs1ep4iMHiOd98x .icon-shape .label rect,#mermaid-svg-yhs1ep4iMHiOd98x .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-yhs1ep4iMHiOd98x .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-yhs1ep4iMHiOd98x .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-yhs1ep4iMHiOd98x :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} running
stopped
安装 Docker
创建 docker-compose.yml
docker compose up -d
容器状态检查
验证连接
查看日志排查
Spring Boot 连接配置

二、部署 PostgreSQL

2.1 docker-compose 方式(推荐)

在 mini-shop 项目根目录创建 docker-compose.yml

yaml 复制代码
version: "3.9"

services:
  postgres:
    image: postgres:16-alpine
    container_name: mini-shop-postgres
    environment:
      POSTGRES_DB: mini_shop
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres123
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data
    restart: unless-stopped

volumes:
  postgres_data:
配置项 说明
image: postgres:16-alpine PostgreSQL 16 的 Alpine 精简版镜像(体积小)
POSTGRES_DB 自动创建的数据库名
POSTGRES_USER / POSTGRES_PASSWORD 用户名和密码
ports: "5432:5432" 宿主机端口 → 容器端口映射
volumes: postgres_data 数据持久化------容器删除后数据不丢失

2.2 启动数据库

bash 复制代码
# 在项目根目录执行
docker compose up -d

# -d 表示后台运行

验证容器状态:

bash 复制代码
docker compose ps

看到 mini-shop-postgres 状态为 running 即可。

2.3 验证数据库连接

bash 复制代码
# 进入 PostgreSQL 交互终端
docker exec -it mini-shop-postgres psql -U postgres -d mini_shop

# 执行 SQL
\l               # 列出所有数据库
\dt              # 列出表(目前为空)
\q               # 退出

2.4 常用 Docker 命令

bash 复制代码
# 停止数据库
docker compose stop

# 启动(已创建的容器)
docker compose start

# 停止并删除容器(数据保留在 volume 中)
docker compose down

# 停止并删除容器 + 删除数据(完全重置)
docker compose down -v

# 查看数据库日志
docker compose logs postgres

三、Spring Boot 连接配置

3.1 添加依赖

确保 build.gradle.kts 中有 JPA 和 PostgreSQL 依赖:

kotlin 复制代码
dependencies {
    // Spring Data JPA(包含 Hibernate 7)
    implementation("org.springframework.boot:spring-boot-starter-data-jpa")

    // PostgreSQL 驱动
    runtimeOnly("org.postgresql:postgresql")

    // 之前的依赖...
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("org.springframework.boot:spring-boot-starter-validation")
}

3.2 配置数据源

修改 application-dev.yml

yaml 复制代码
spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/mini_shop
    username: postgres
    password: postgres123
    driver-class-name: org.postgresql.Driver

  jpa:
    # 自动根据实体类生成/更新表结构(开发环境用,生产环境关闭!)
    hibernate:
      ddl-auto: update

    # 在控制台打印 SQL 语句(开发环境调试用)
    show-sql: true

    properties:
      hibernate:
        # 格式化 SQL 输出
        format_sql: true
        # 指定数据库方言
        dialect: org.hibernate.dialect.PostgreSQLDialect

3.3 ddl-auto 各值说明

行为 适用环境
none 不做任何操作 生产
validate 只验证实体与表结构是否匹配,不修改 生产
update 根据实体自动更新表结构(新增列/表) 开发
create 每次启动删除并重建所有表 测试(会丢数据!)
create-drop 启动时创建,退出时删除 测试

重要警告ddl-auto: update 仅限开发环境!生产环境必须用 validatenone,通过 Liquibase(第 22 篇)管理表结构变更。

3.4 JPA 与 Hibernate 的关系

复制代码
JPA(Jakarta Persistence API)
  ← 标准规范(接口),定义了 ORM 的标准 API
  
Hibernate
  ← JPA 的实现之一(最流行的实现)
  
Spring Data JPA
  ← 在 JPA 之上再加一层封装(Repository 接口自动实现)

层级关系:Spring Data JPA → JPA(接口)→ Hibernate(实现)→ 数据库


下面是 JPA 与 Hibernate 的层级关系图:
#mermaid-svg-aOFh1i5SV08uEMB8{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-aOFh1i5SV08uEMB8 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-aOFh1i5SV08uEMB8 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-aOFh1i5SV08uEMB8 .error-icon{fill:#552222;}#mermaid-svg-aOFh1i5SV08uEMB8 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-aOFh1i5SV08uEMB8 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-aOFh1i5SV08uEMB8 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-aOFh1i5SV08uEMB8 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-aOFh1i5SV08uEMB8 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-aOFh1i5SV08uEMB8 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-aOFh1i5SV08uEMB8 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-aOFh1i5SV08uEMB8 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-aOFh1i5SV08uEMB8 .marker.cross{stroke:#333333;}#mermaid-svg-aOFh1i5SV08uEMB8 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-aOFh1i5SV08uEMB8 p{margin:0;}#mermaid-svg-aOFh1i5SV08uEMB8 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-aOFh1i5SV08uEMB8 .cluster-label text{fill:#333;}#mermaid-svg-aOFh1i5SV08uEMB8 .cluster-label span{color:#333;}#mermaid-svg-aOFh1i5SV08uEMB8 .cluster-label span p{background-color:transparent;}#mermaid-svg-aOFh1i5SV08uEMB8 .label text,#mermaid-svg-aOFh1i5SV08uEMB8 span{fill:#333;color:#333;}#mermaid-svg-aOFh1i5SV08uEMB8 .node rect,#mermaid-svg-aOFh1i5SV08uEMB8 .node circle,#mermaid-svg-aOFh1i5SV08uEMB8 .node ellipse,#mermaid-svg-aOFh1i5SV08uEMB8 .node polygon,#mermaid-svg-aOFh1i5SV08uEMB8 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-aOFh1i5SV08uEMB8 .rough-node .label text,#mermaid-svg-aOFh1i5SV08uEMB8 .node .label text,#mermaid-svg-aOFh1i5SV08uEMB8 .image-shape .label,#mermaid-svg-aOFh1i5SV08uEMB8 .icon-shape .label{text-anchor:middle;}#mermaid-svg-aOFh1i5SV08uEMB8 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-aOFh1i5SV08uEMB8 .rough-node .label,#mermaid-svg-aOFh1i5SV08uEMB8 .node .label,#mermaid-svg-aOFh1i5SV08uEMB8 .image-shape .label,#mermaid-svg-aOFh1i5SV08uEMB8 .icon-shape .label{text-align:center;}#mermaid-svg-aOFh1i5SV08uEMB8 .node.clickable{cursor:pointer;}#mermaid-svg-aOFh1i5SV08uEMB8 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-aOFh1i5SV08uEMB8 .arrowheadPath{fill:#333333;}#mermaid-svg-aOFh1i5SV08uEMB8 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-aOFh1i5SV08uEMB8 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-aOFh1i5SV08uEMB8 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-aOFh1i5SV08uEMB8 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-aOFh1i5SV08uEMB8 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-aOFh1i5SV08uEMB8 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-aOFh1i5SV08uEMB8 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-aOFh1i5SV08uEMB8 .cluster text{fill:#333;}#mermaid-svg-aOFh1i5SV08uEMB8 .cluster span{color:#333;}#mermaid-svg-aOFh1i5SV08uEMB8 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-aOFh1i5SV08uEMB8 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-aOFh1i5SV08uEMB8 rect.text{fill:none;stroke-width:0;}#mermaid-svg-aOFh1i5SV08uEMB8 .icon-shape,#mermaid-svg-aOFh1i5SV08uEMB8 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-aOFh1i5SV08uEMB8 .icon-shape p,#mermaid-svg-aOFh1i5SV08uEMB8 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-aOFh1i5SV08uEMB8 .icon-shape .label rect,#mermaid-svg-aOFh1i5SV08uEMB8 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-aOFh1i5SV08uEMB8 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-aOFh1i5SV08uEMB8 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-aOFh1i5SV08uEMB8 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 数据层
实现层
规范层
应用层
调用
标准接口
JDBC 驱动
Spring Data JPA

Repository 接口
JPA

Jakarta Persistence API
Hibernate 7

ORM 框架
PostgreSQL 数据库

四、HikariCP 连接池

4.1 为什么需要连接池

每次数据库操作都创建/销毁 TCP 连接开销很大。连接池预先创建一批连接并复用:

复制代码
没有连接池:每次请求 → 创建连接 → 执行 SQL → 销毁连接(慢)
有连接池:  每次请求 → 从池中借连接 → 执行 SQL → 归还到池中(快)

4.2 Spring Boot 默认连接池

Spring Boot 默认使用 HikariCP------目前性能最好的 JDBC 连接池,不需要额外配置。

4.3 关键参数调优

yaml 复制代码
spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/mini_shop
    username: postgres
    password: postgres123
    hikari:
      # 最大连接数(默认 10,生产环境建议 = CPU 核心数 * 2 + 磁盘数)
      maximum-pool-size: 10
      # 最小空闲连接数(默认 = maximum-pool-size)
      minimum-idle: 5
      # 连接超时时间(默认 30 秒)
      connection-timeout: 30000
      # 连接最长存活时间(默认 30 分钟)
      max-lifetime: 1800000
      # 空闲连接超时(默认 10 分钟)
      idle-timeout: 600000
      # 连接池名称
      pool-name: MiniShopHikariPool

入门建议:开发阶段用默认配置即可。生产调优在模块八(性能调优)中详细讲解。


下面是连接池工作原理的对比图:
#mermaid-svg-RftfIT9BdbUFhqaa{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-RftfIT9BdbUFhqaa .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-RftfIT9BdbUFhqaa .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-RftfIT9BdbUFhqaa .error-icon{fill:#552222;}#mermaid-svg-RftfIT9BdbUFhqaa .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-RftfIT9BdbUFhqaa .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-RftfIT9BdbUFhqaa .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-RftfIT9BdbUFhqaa .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-RftfIT9BdbUFhqaa .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-RftfIT9BdbUFhqaa .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-RftfIT9BdbUFhqaa .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-RftfIT9BdbUFhqaa .marker{fill:#333333;stroke:#333333;}#mermaid-svg-RftfIT9BdbUFhqaa .marker.cross{stroke:#333333;}#mermaid-svg-RftfIT9BdbUFhqaa svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-RftfIT9BdbUFhqaa p{margin:0;}#mermaid-svg-RftfIT9BdbUFhqaa .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-RftfIT9BdbUFhqaa .cluster-label text{fill:#333;}#mermaid-svg-RftfIT9BdbUFhqaa .cluster-label span{color:#333;}#mermaid-svg-RftfIT9BdbUFhqaa .cluster-label span p{background-color:transparent;}#mermaid-svg-RftfIT9BdbUFhqaa .label text,#mermaid-svg-RftfIT9BdbUFhqaa span{fill:#333;color:#333;}#mermaid-svg-RftfIT9BdbUFhqaa .node rect,#mermaid-svg-RftfIT9BdbUFhqaa .node circle,#mermaid-svg-RftfIT9BdbUFhqaa .node ellipse,#mermaid-svg-RftfIT9BdbUFhqaa .node polygon,#mermaid-svg-RftfIT9BdbUFhqaa .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-RftfIT9BdbUFhqaa .rough-node .label text,#mermaid-svg-RftfIT9BdbUFhqaa .node .label text,#mermaid-svg-RftfIT9BdbUFhqaa .image-shape .label,#mermaid-svg-RftfIT9BdbUFhqaa .icon-shape .label{text-anchor:middle;}#mermaid-svg-RftfIT9BdbUFhqaa .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-RftfIT9BdbUFhqaa .rough-node .label,#mermaid-svg-RftfIT9BdbUFhqaa .node .label,#mermaid-svg-RftfIT9BdbUFhqaa .image-shape .label,#mermaid-svg-RftfIT9BdbUFhqaa .icon-shape .label{text-align:center;}#mermaid-svg-RftfIT9BdbUFhqaa .node.clickable{cursor:pointer;}#mermaid-svg-RftfIT9BdbUFhqaa .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-RftfIT9BdbUFhqaa .arrowheadPath{fill:#333333;}#mermaid-svg-RftfIT9BdbUFhqaa .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-RftfIT9BdbUFhqaa .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-RftfIT9BdbUFhqaa .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RftfIT9BdbUFhqaa .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-RftfIT9BdbUFhqaa .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RftfIT9BdbUFhqaa .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-RftfIT9BdbUFhqaa .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-RftfIT9BdbUFhqaa .cluster text{fill:#333;}#mermaid-svg-RftfIT9BdbUFhqaa .cluster span{color:#333;}#mermaid-svg-RftfIT9BdbUFhqaa div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-RftfIT9BdbUFhqaa .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-RftfIT9BdbUFhqaa rect.text{fill:none;stroke-width:0;}#mermaid-svg-RftfIT9BdbUFhqaa .icon-shape,#mermaid-svg-RftfIT9BdbUFhqaa .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RftfIT9BdbUFhqaa .icon-shape p,#mermaid-svg-RftfIT9BdbUFhqaa .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-RftfIT9BdbUFhqaa .icon-shape .label rect,#mermaid-svg-RftfIT9BdbUFhqaa .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RftfIT9BdbUFhqaa .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-RftfIT9BdbUFhqaa .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-RftfIT9BdbUFhqaa :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 慢
有连接池(HikariCP)
请求到达
从池中借连接
执行 SQL
归还到池中
响应返回
没有连接池
请求到达
创建 TCP 连接
执行 SQL
销毁连接
响应返回

五、连接验证

5.1 启动项目验证

确保 Docker 中 PostgreSQL 已启动,然后运行 Spring Boot 项目。

控制台看到类似日志说明连接成功:

复制代码
HikariPool-1 - Starting...
HikariPool-1 - Added connection org.postgresql.jdbc.PgConnection@...
HikariPool-1 - Start completed.

5.2 常见连接问题排查

问题一:Connection refused
复制代码
Connection refused: localhost/127.0.0.1:5432

原因:PostgreSQL 容器没有启动。

bash 复制代码
# 检查容器状态
docker compose ps

# 如果没有运行,启动它
docker compose up -d
问题二:Authentication failed
复制代码
FATAL: password authentication failed for user "postgres"

原因:密码不匹配。检查 application-dev.yml 中的密码与 docker-compose.yml 中的 POSTGRES_PASSWORD 是否一致。

如果修改了 docker-compose.yml 中的密码,需要先 docker compose down -v(删除旧数据卷)再重新 docker compose up -d

问题三:Database does not exist
复制代码
FATAL: database "mini_shop" does not exist

原因:数据库名不匹配。检查 URL 中的数据库名与 POSTGRES_DB 是否一致。


下面是连接问题排查流程图:
#mermaid-svg-b8HWzdkzerz55MSa{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-b8HWzdkzerz55MSa .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-b8HWzdkzerz55MSa .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-b8HWzdkzerz55MSa .error-icon{fill:#552222;}#mermaid-svg-b8HWzdkzerz55MSa .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-b8HWzdkzerz55MSa .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-b8HWzdkzerz55MSa .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-b8HWzdkzerz55MSa .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-b8HWzdkzerz55MSa .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-b8HWzdkzerz55MSa .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-b8HWzdkzerz55MSa .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-b8HWzdkzerz55MSa .marker{fill:#333333;stroke:#333333;}#mermaid-svg-b8HWzdkzerz55MSa .marker.cross{stroke:#333333;}#mermaid-svg-b8HWzdkzerz55MSa svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-b8HWzdkzerz55MSa p{margin:0;}#mermaid-svg-b8HWzdkzerz55MSa .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-b8HWzdkzerz55MSa .cluster-label text{fill:#333;}#mermaid-svg-b8HWzdkzerz55MSa .cluster-label span{color:#333;}#mermaid-svg-b8HWzdkzerz55MSa .cluster-label span p{background-color:transparent;}#mermaid-svg-b8HWzdkzerz55MSa .label text,#mermaid-svg-b8HWzdkzerz55MSa span{fill:#333;color:#333;}#mermaid-svg-b8HWzdkzerz55MSa .node rect,#mermaid-svg-b8HWzdkzerz55MSa .node circle,#mermaid-svg-b8HWzdkzerz55MSa .node ellipse,#mermaid-svg-b8HWzdkzerz55MSa .node polygon,#mermaid-svg-b8HWzdkzerz55MSa .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-b8HWzdkzerz55MSa .rough-node .label text,#mermaid-svg-b8HWzdkzerz55MSa .node .label text,#mermaid-svg-b8HWzdkzerz55MSa .image-shape .label,#mermaid-svg-b8HWzdkzerz55MSa .icon-shape .label{text-anchor:middle;}#mermaid-svg-b8HWzdkzerz55MSa .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-b8HWzdkzerz55MSa .rough-node .label,#mermaid-svg-b8HWzdkzerz55MSa .node .label,#mermaid-svg-b8HWzdkzerz55MSa .image-shape .label,#mermaid-svg-b8HWzdkzerz55MSa .icon-shape .label{text-align:center;}#mermaid-svg-b8HWzdkzerz55MSa .node.clickable{cursor:pointer;}#mermaid-svg-b8HWzdkzerz55MSa .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-b8HWzdkzerz55MSa .arrowheadPath{fill:#333333;}#mermaid-svg-b8HWzdkzerz55MSa .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-b8HWzdkzerz55MSa .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-b8HWzdkzerz55MSa .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-b8HWzdkzerz55MSa .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-b8HWzdkzerz55MSa .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-b8HWzdkzerz55MSa .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-b8HWzdkzerz55MSa .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-b8HWzdkzerz55MSa .cluster text{fill:#333;}#mermaid-svg-b8HWzdkzerz55MSa .cluster span{color:#333;}#mermaid-svg-b8HWzdkzerz55MSa div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-b8HWzdkzerz55MSa .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-b8HWzdkzerz55MSa rect.text{fill:none;stroke-width:0;}#mermaid-svg-b8HWzdkzerz55MSa .icon-shape,#mermaid-svg-b8HWzdkzerz55MSa .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-b8HWzdkzerz55MSa .icon-shape p,#mermaid-svg-b8HWzdkzerz55MSa .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-b8HWzdkzerz55MSa .icon-shape .label rect,#mermaid-svg-b8HWzdkzerz55MSa .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-b8HWzdkzerz55MSa .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-b8HWzdkzerz55MSa .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-b8HWzdkzerz55MSa :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是





连接失败
Connection refused?
检查容器是否运行

docker compose ps
未运行则启动

docker compose up -d
Authentication failed?
检查密码是否一致

application-dev.yml ↔ docker-compose.yml
修改密码后

docker compose down -v 再重启
Database does not exist?
检查数据库名是否一致

URL 中的库名 ↔ POSTGRES_DB
查看完整日志

docker compose logs postgres
连接成功 ✅

六、小结与项目结构

本篇完成后,mini-shop 项目结构:

复制代码
mini-shop/
├── docker-compose.yml           ← PostgreSQL 容器配置
├── build.gradle.kts
├── src/main/
│   ├── kotlin/com/example/minishop/
│   │   ├── MiniShopApplication.kt
│   │   ├── controller/
│   │   ├── service/
│   │   ├── repository/
│   │   ├── dto/
│   │   └── config/
│   └── resources/
│       ├── application.yml
│       └── application-dev.yml  ← 数据库连接配置

本篇小结

知识点 核心内容
Docker 部署 PostgreSQL docker compose up -d 一键启动
数据持久化 Docker Volume 保证容器删除后数据不丢
连接配置 spring.datasource 配置 URL/用户名/密码
ddl-auto update(开发)/ validate(生产)
HikariCP Spring Boot 默认连接池,高性能
JPA → Hibernate 规范 → 实现的关系
连接池原理 预创建连接复用,避免频繁创建/销毁

下篇预告

第 18 篇:Hibernate 7 与 JPA --- 实体映射基础

如何把 Kotlin 类映射为数据库表?@Entity / @Table / @Id / @Column 各自的作用?下一篇将 mini-shop 的商品模型映射到 PostgreSQL。


如果本篇内容对你有帮助,欢迎点赞收藏!有任何疑问,欢迎在评论区交流。

相关推荐
plainGeekDev2 小时前
JUnit 4 → JUnit 5 + Kotlin
android·java·kotlin
plainGeekDev2 小时前
Mockito → MockK
android·java·kotlin
深色風信子3 小时前
Kotlin Bytedeco OpenCV 图像图像58 无缝克隆
开发语言·opencv·kotlin·bytedeco
我命由我123454 小时前
Kotlin 面向对象 - 枚举排序
android·java·开发语言·java-ee·kotlin·android studio·android-studio
红尘伴蝶舞19 小时前
Android 17 局域网调试与 ACCESS_LOCAL_NETWORK 权限隔离实践
kotlin·测试·设计
plainGeekDev1 天前
运行时获取依赖 → 编译时注入
android·java·kotlin
杉氧1 天前
用 Compose 挑战交互与动效天花板:ComposeCraftLab 开源实验室全解析
android·前端·kotlin
刘名喜1 天前
第09篇-协程基础-Kotlin异步编程
开发语言·kotlin·springboot
刘名喜1 天前
第12篇-Gradle-Kotlin-DSL构建指南
开发语言·kotlin·springboot