综合练习
设计 tracks 表 PG 版;可选 Spring 只读 API。
45 分钟
综合练习:tracks schema 与只读 API
这一课你将能做到
- 在 PostgreSQL 中独立落地
app.cf_track/app.cf_lesson子集(约束、timestamptz、JSONB、索引齐全) - 插入与 sql-essentials 概念一致的种子数据,并用 JOIN / JSONB / EXPLAIN 自检
- (可选)用 Spring
pg-dev暴露与生产隔离的只读GET /api/pg/tracks
为什么要学
前几课拆开练:安装、DDL、类型、JSONB、索引、事务、Spring、迁移、选型、Compose。本课收束成一条可演示的闭环——像本站课程模型,但方言是 PG。
验收标准写清楚,避免「自以为会」;可选 API 证明应用层也通。仍不修改生产 MySQL。
先建立图像
验收闭环:
Compose/容器 PG
│
▼
Flyway 或手跑 V1 DDL → app.cf_track / cf_lesson
│
▼
种子数据(≥3 track,每门 ≥2 lesson)
│
├─ SQL:JOIN 统计、JSONB @>、slug 的 EXPLAIN
└─(可选)Spring GET /api/pg/tracks
| 验收项 | 标准 |
|---|---|
| DDL | 无 MySQL 专有类型;有 PK/UK/FK;布尔用 BOOLEAN |
| 扩展 | metadata JSONB + GIN |
| 查询 | 一条 JOIN 聚合 + 一条 @> |
| 计划 | slug = ? 走索引类节点 |
| API | 可选;路径勿覆盖生产 /api/tracks |
跟着做
步骤 1:环境拉起
用 pg-docker 的 Compose 启动,psql 连入 codeforge_pg。确认与 MySQL 3306 无冲突。
步骤 2:落地 Schema
参考本站 backend/src/main/resources/schema.sql 改写方言(不要原样粘贴):
CREATE SCHEMA IF NOT EXISTS app;
CREATE TABLE app.cf_track (
id BIGSERIAL PRIMARY KEY,
slug VARCHAR(64) NOT NULL,
title VARCHAR(255) NOT NULL,
description TEXT,
category VARCHAR(32) NOT NULL DEFAULT 'language',
level VARCHAR(16) NOT NULL DEFAULT 'beginner',
sort_order INT NOT NULL DEFAULT 0,
is_member BOOLEAN NOT NULL DEFAULT FALSE,
metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT uq_track_slug UNIQUE (slug),
CONSTRAINT ck_track_level
CHECK (level IN ('beginner', 'intermediate', 'advanced'))
);
CREATE TABLE app.cf_lesson (
id BIGSERIAL PRIMARY KEY,
track_id BIGINT NOT NULL REFERENCES app.cf_track (id) ON DELETE CASCADE,
slug VARCHAR(64) NOT NULL,
title VARCHAR(255) NOT NULL,
order_index INT NOT NULL DEFAULT 0,
content_md TEXT,
status VARCHAR(16) NOT NULL DEFAULT 'draft',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT uq_lesson_track_slug UNIQUE (track_id, slug)
);
CREATE INDEX idx_track_cat_sort ON app.cf_track (category, sort_order);
CREATE INDEX idx_track_metadata_gin ON app.cf_track USING GIN (metadata);