PostgreSQL 入门与实践:从零搭建一个 CRUD 应用
一、PostgreSQL 简介
PostgreSQL 是一个功能强大的开源对象关系型数据库系统,拥有超过 30 年的活跃开发历史。它以其可靠性、功能丰富性和对 SQL 标准的高度兼容性而闻名。
核心特性
- 开源免费:PostgreSQL 采用 PostgreSQL 许可证,类似于 MIT 或 BSD,可以自由使用、修改和分发。
- ACID 事务:完全支持原子性、一致性、隔离性和持久性,确保数据安全可靠。
- 丰富的数据类型:除了标准类型,还支持 JSON/JSONB、数组、hstore(键值存储)、UUID、几何类型等。
- 扩展性:支持自定义函数、操作符、数据类型和索引方法。流行的扩展如 PostGIS(地理空间)和 pgvector(向量搜索)使其应用场景远超传统数据库。
- 并发控制:使用 MVCC(多版本并发控制)实现高并发读写,读不阻塞写,写不阻塞读。
- 高级索引:支持 B-tree、Hash、GiST、GIN、BRIN 等多种索引类型。
- 视图、触发器、存储过程:支持用多种语言(PL/pgSQL、PL/Python、PL/Perl 等)编写函数和触发器。
与其他数据库的比较
| 特性 | PostgreSQL | MySQL | Oracle |
|---|---|---|---|
| 许可证 | 开源(类 MIT) | 开源(GPL)/ 商业版 | 商业收费 |
| SQL 标准兼容性 | 极高 | 中等 | 极高 |
| JSON 支持 | JSONB(二进制,可索引) | JSON(5.7+) | JSON |
| 并发控制 | MVCC(优秀) | MVCC | MVCC |
| 扩展生态 | PostGIS、pgvector 等 | 丰富但有限 | 商业扩展 |
| 适用场景 | 复杂查询、地理空间、分析 | Web 应用(LAMP 栈) | 企业级应用 |
二、为什么选择 PostgreSQL?
- SQL 标准兼容性:PostgreSQL 是最接近 SQL 标准的关系型数据库,迁移成本低。
- JSONB 支持:可以同时作为关系型数据库和文档数据库使用,减少技术栈复杂度。
- 地理空间能力:通过 PostGIS 扩展,PostgreSQL 是业界领先的地理空间数据库。
- 社区活跃:版本迭代快,每个大版本都有显著性能提升和新特性。
- 云原生支持:各大云厂商(AWS RDS、Google Cloud SQL、Azure Database、阿里云 RDS)都提供托管服务。
三、项目实战:Spring Boot + PostgreSQL CRUD 应用
下面我们基于 postgres-demo 项目,演示如何在实际项目中集成和使用 PostgreSQL。
项目技术栈
- Java 8 + Spring Boot 2.7.18
- Spring Data JPA(Hibernate ORM)
- PostgreSQL 15(Docker 部署)
- Maven 构建
项目结构
postgres-demo/
├── docker-compose.yml # PostgreSQL + 应用容器编排
├── Dockerfile # 应用镜像构建
├── pom.xml # Maven 依赖配置
├── src/main/resources/
│ ├── application.yml # 数据库连接配置
│ └── db/
│ ├── schema.sql # 建表脚本
│ └── data.sql # 初始化数据
└── src/main/java/.../
├── entity/User.java # JPA 实体
├── repository/UserRepository.java # 数据访问层
├── service/UserService.java # 业务逻辑层
└── controller/UserController.java # REST API
配置数据库连接
yaml
# application.yml
spring:
datasource:
url: jdbc:postgresql://${DB_HOST:localhost}:${DB_PORT:5432}/${DB_NAME:postgres}
username: ${DB_USER:postgres}
password: ${DB_PASSWORD:postgres}
driver-class-name: org.postgresql.Driver
jpa:
hibernate:
ddl-auto: none # 手动管理表结构
show-sql: true
open-in-view: false
sql:
init:
mode: always
schema-locations: classpath:db/schema.sql
data-locations: classpath:db/data.sql
数据库连接信息通过环境变量注入,支持
DB_HOST、DB_PORT、DB_NAME、DB_USER、DB_PASSWORD,并提供了本地开发的默认值。
数据库表设计
sql
-- schema.sql
CREATE TABLE IF NOT EXISTS app_user (
id BIGSERIAL PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
email VARCHAR(100) NOT NULL,
age INT,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
sql
-- data.sql
INSERT INTO app_user (username, email, age, created_at) VALUES
('zhangsan', 'zhangsan@example.com', 28, NOW()),
('lisi', 'lisi@example.com', 32, NOW()),
('wangwu', 'wangwu@example.com', 25, NOW())
ON CONFLICT (username) DO NOTHING;
使用 CREATE TABLE IF NOT EXISTS 和 ON CONFLICT DO NOTHING 确保脚本幂等,可以安全地重复执行。
JPA 实体映射
java
@Entity
@Table(name = "app_user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true, length = 50)
private String username;
@Column(nullable = false, length = 100)
private String email;
private Integer age;
@Column(name = "created_at", nullable = false, updatable = false)
private LocalDateTime createdAt = LocalDateTime.now();
}
GenerationType.IDENTITY对应 PostgreSQL 的BIGSERIAL自增列updatable = false防止created_at被意外修改
数据访问层
java
public interface UserRepository extends JpaRepository<User, Long> {
boolean existsByUsername(String username);
}
继承 JpaRepository 即可获得完整的 CRUD 能力,自定义方法 existsByUsername 由 Spring Data JPA 自动实现。
业务逻辑层
java
@Service
@Transactional
public class UserService {
private final UserRepository userRepository;
@Transactional(readOnly = true)
public List<User> findAll() {
return userRepository.findAll();
}
public User create(User user) {
if (userRepository.existsByUsername(user.getUsername())) {
throw new IllegalArgumentException("用户名已存在: " + user.getUsername());
}
return userRepository.save(user);
}
public void delete(Long id) {
userRepository.deleteById(id);
}
}
- 类级别
@Transactional确保所有方法在事务中执行 - 查询方法标记
readOnly = true,优化数据库连接 - 业务校验(如重名校验)在 Service 层完成
REST API 接口
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/users |
查询所有用户 |
| GET | /api/users/{id} |
查询单个用户 |
| POST | /api/users |
创建用户 |
| PUT | /api/users/{id} |
更新用户 |
| DELETE | /api/users/{id} |
删除用户 |
Docker 一键部署
yaml
# docker-compose.yml
services:
db:
image: postgres:15-alpine
container_name: postgres-demo-db
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: postgres
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 10
app:
build: .
container_name: postgres-demo-app
environment:
DB_HOST: db
DB_PORT: 5432
DB_NAME: postgres
DB_USER: postgres
DB_PASSWORD: postgres
ports:
- "8080:8080"
depends_on:
db:
condition: service_healthy
运行一条命令即可启动:
bash
docker compose up --build
验证 API
bash
# 查询所有用户
curl http://localhost:8080/api/users
# 创建新用户
curl -X POST http://localhost:8080/api/users \
-H "Content-Type: application/json" \
-d '{"username":"zhaoliu","email":"zhaoliu@example.com","age":30}'
# 删除用户
curl -X DELETE http://localhost:8080/api/users/1
四、总结
PostgreSQL 是一个功能强大、生态丰富的开源数据库,无论是简单的 CRUD 应用还是复杂的数据分析场景,它都能胜任。通过本文的实战项目,你可以看到:
- 使用 Docker Compose 可以轻松搭建 PostgreSQL 开发环境
- Spring Boot + Spring Data JPA 提供了简洁的数据库操作方式
- 通过 SQL 脚本管理表结构,比 Hibernate 自动 DDL 更可控
- 环境变量配置让应用在不同环境(开发、测试、生产)间灵活切换
如果你正在为下一个项目选择数据库,PostgreSQL 绝对值得认真考虑。