PostgreSQL 入门与实践:从零搭建一个 CRUD 应用

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?

  1. SQL 标准兼容性:PostgreSQL 是最接近 SQL 标准的关系型数据库,迁移成本低。
  2. JSONB 支持:可以同时作为关系型数据库和文档数据库使用,减少技术栈复杂度。
  3. 地理空间能力:通过 PostGIS 扩展,PostgreSQL 是业界领先的地理空间数据库。
  4. 社区活跃:版本迭代快,每个大版本都有显著性能提升和新特性。
  5. 云原生支持:各大云厂商(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 应用还是复杂的数据分析场景,它都能胜任。通过本文的实战项目,你可以看到:

  1. 使用 Docker Compose 可以轻松搭建 PostgreSQL 开发环境
  2. Spring Boot + Spring Data JPA 提供了简洁的数据库操作方式
  3. 通过 SQL 脚本管理表结构,比 Hibernate 自动 DDL 更可控
  4. 环境变量配置让应用在不同环境(开发、测试、生产)间灵活切换

如果你正在为下一个项目选择数据库,PostgreSQL 绝对值得认真考虑。


项目源码:https://atomgit.com/wangchunyu114/postgres-demo

相关推荐
晚风叙码1 小时前
MySQL 表的约束详解:从空属性到外键
android·数据库·mysql·adb
IT古董9 小时前
《FDE前沿部署工程师实战教程》33 - Enterprise AI Observability:从Agent Trace到全链路智能运维
数据库·人工智能
for_ever_love__9 小时前
MySQL 主从复制讲透:binlog 原理、搭建步骤与读写分离
java·数据库·mysql·binlog·读写分离·原理·主从复制
Nturmoils13 小时前
GROUP BY 先别想当然,查汇总前把规则跑清楚
数据库
最笨的羊羊14 小时前
Oceanbase数据库系列之:向量数据库核心知识
数据库·oceanbase
fish_xk14 小时前
mysql中的表的约束
数据库·mysql
谢亮_vipxieliang14 小时前
Go Channel 高级模式——从底层原理到扇出扇入实战
java·数据库·golang
ggaofeng15 小时前
自己编写邮件服务器和客户端
数据库
java1234_小锋15 小时前
【技术专题】Mysql8 数据库 - Mysql8 添加,更新,删除数据
数据库·mysql
PaperData15 小时前
2015-2024年各省绿色贸易数据
数据库