在启动时为开发环境预填充带模式和数据的数据库

在本地开发期间使用必要的数据和模式预填充数据库是一种常见的做法,可以增强开发和测试工作流程。通过模拟真实世界的场景,这种做法有助于及早发现前端问题,确保数据库管理员和软件工程师之间的协作一致,并促进更顺畅的协作。预填充的好处包括自信的部署、跨环境的一致性以及早期问题检测,最终改进了整体开发过程。

在本指南中,你将学习如何

  • 使用 Docker 启动一个 Postgres 容器
  • 使用 SQL 脚本预填充 Postgres
  • 通过将 SQL 文件复制到 Docker 镜像中预填充 Postgres
  • 使用 JavaScript 代码预填充 Postgres

将 Postgres 与 Docker 配合使用

Postgres 的官方 Docker 镜像 提供了一种便捷的方式,可在你的开发机器上运行 Postgres 数据库。Postgres Docker 镜像是一个预配置的环境,封装了 PostgreSQL 数据库系统。它是一个独立的单元,可随时在 Docker 容器中运行。通过使用此镜像,你可以快速轻松地设置一个 Postgres 实例,而无需手动配置。

前提条件

要遵循本操作指南,需要满足以下前提条件

启动 Postgres

使用以下步骤快速启动 Postgres 演示

  1. 打开终端并运行以下命令以启动 Postgres 容器。

    此示例将启动一个 Postgres 容器,将端口 5432 暴露到主机上,以便本地运行的应用程序可以使用密码 mysecretpassword 连接到它。

    $ docker run -d --name postgres -p 5432:5432 -e POSTGRES_PASSWORD=mysecretpassword postgres
    
  2. 通过在 Docker Dashboard 中选择容器并检查日志来验证 Postgres 是否已启动并正在运行。

    PostgreSQL Database directory appears to contain a database; Skipping initialization
    
    2024-09-08 09:09:47.136 UTC [1] LOG:  starting PostgreSQL 16.4 (Debian 16.4-1.pgdg120+1) on aarch64-unknown-linux-gnu, compiled by gcc (Debian 12.2.0-14) 12.2.0, 64-bit
    2024-09-08 09:09:47.137 UTC [1] LOG:  listening on IPv4 address "0.0.0.0", port 5432
    2024-09-08 09:09:47.137 UTC [1] LOG:  listening on IPv6 address "::", port 5432
    2024-09-08 09:09:47.139 UTC [1] LOG:  listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
    2024-09-08 09:09:47.142 UTC [29] LOG:  database system was shut down at 2024-09-08 09:07:09 UTC
    2024-09-08 09:09:47.148 UTC [1] LOG:  database system is ready to accept connections
  3. 从本地系统连接到 Postgres。

    psql 是 PostgreSQL 交互式 shell,用于连接到 Postgres 数据库并允许你开始执行 SQL 命令。假设你的本地系统上已经安装了 psql 工具,现在是时候连接到 Postgres 数据库了。在你的本地终端上运行以下命令

    $ docker exec -it postgres psql -h localhost -U postgres
    

    你现在可以在 psql 提示符中执行任何需要的 SQL 查询或命令。

    使用 \q\quit 从 Postgres 交互式 shell 退出。

使用 SQL 脚本预填充 Postgres 数据库

现在你已经熟悉了 Postgres,是时候看看如何用示例数据预填充它了。在此演示中,你将首先创建一个包含 SQL 命令的脚本。该脚本定义了数据库和表结构,并插入了示例数据。然后,你将连接到数据库以验证数据。

假设你已经有一个运行中的 Postgres 数据库实例,请按照以下步骤预填充数据库。

  1. 创建一个名为 seed.sql 的空文件,并添加以下内容。

    CREATE DATABASE sampledb;
    
    \c sampledb
    
    CREATE TABLE users (
      id SERIAL PRIMARY KEY,
      name VARCHAR(50),
      email VARCHAR(100) UNIQUE
    );
    
    INSERT INTO users (name, email) VALUES
      ('Alpha', 'alpha@example.com'),
      ('Beta', 'beta@example.com'),
      ('Gamma', 'gamma@example.com');  

    该 SQL 脚本创建一个名为 sampledb 的新数据库,连接到它,并创建一个 users 表。该表包含一个自增的 id 作为主键,一个最大长度为 50 个字符的 name 字段,以及一个最多 100 个字符的唯一 email 字段。

    创建表后,INSERT 命令将三个用户及其对应的姓名和电子邮件插入到 users 表中。此设置构成了一个基本数据库结构,用于存储具有唯一电子邮件地址的用户信息。

  2. 预填充数据库。

    现在是时候使用 < 运算符将 seed.sql 的内容直接输入到数据库中了。此命令用于对名为 sampledb 的 Postgres 数据库执行名为 seed.sql 的 SQL 脚本。

    $ cat seed.sql | docker exec -i postgres psql -h localhost -U postgres -f-
    

    查询执行后,你将看到以下结果

    CREATE DATABASE
    You are now connected to database "sampledb" as user "postgres".
    CREATE TABLE
    INSERT 0 3
  3. 运行以下 psql 命令,验证名为 users 的表是否已在数据库 sampledb 中填充。

    $ docker exec -it postgres psql -h localhost -U postgres sampledb
    

    你现在可以在 psql shell 中运行 \l 来列出 Postgres 服务器上的所有数据库。

    sampledb=# \l
                                                 List of databases
    Name    |  Owner   | Encoding |  Collate   |   Ctype    | ICU Locale | Locale Provider |   Access privileges
    -----------+----------+----------+------------+------------+------------+-----------------+-----------------------
    postgres  | postgres | UTF8     | en_US.utf8 | en_US.utf8 |            | libc            |
    sampledb  | postgres | UTF8     | en_US.utf8 | en_US.utf8 |            | libc            |
    template0 | postgres | UTF8     | en_US.utf8 | en_US.utf8 |            | libc            | =c/postgres          +
              |          |          |            |            |            |                 | postgres=CTc/postgres
    template1 | postgres | UTF8     | en_US.utf8 | en_US.utf8 |            | libc            | =c/postgres          +
              |          |          |            |            |            |                 | postgres=CTc/postgres
    (4 rows)
    

    要从 users 表中检索所有数据,请输入以下查询

    sampledb=# SELECT * FROM users;
    id | name  |       email
    ----+-------+-------------------
     1 | Alpha | alpha@example.com
     2 | Beta  | beta@example.com
     3 | Gamma | gamma@example.com
    (3 rows)
    

    使用 \q\quit 从 Postgres 交互式 shell 退出。

通过绑定挂载 SQL 脚本预填充数据库

在 Docker 中,挂载(mounting)是指使主机系统中的文件或目录可以在容器内访问。这使你可以在主机和容器之间共享数据或配置文件,从而实现更大的灵活性和持久性。

现在你已经学习了如何启动 Postgres 并使用 SQL 脚本预填充数据库,是时候学习如何将 SQL 文件直接挂载到 Postgres 容器的初始化目录 (/docker-entrypoint-initdb.d) 中了。/docker-entrypoint-initdb.d 是 PostgreSQL Docker 容器中的一个特殊目录,用于在容器首次启动时初始化数据库。

在执行这些步骤之前,请确保停止所有正在运行的 Postgres 容器(以及卷),以防止端口冲突。

$ docker container stop postgres
  1. 使用以下条目修改 seed.sql

    CREATE TABLE IF NOT EXISTS users (
     id SERIAL PRIMARY KEY,
     name VARCHAR(50),
     email VARCHAR(100) UNIQUE
    );
    
    INSERT INTO users (name, email) VALUES
     ('Alpha', 'alpha@example.com'),
     ('Beta', 'beta@example.com'),
     ('Gamma', 'gamma@example.com')
    ON CONFLICT (email) DO NOTHING;
  2. 创建一个名为 Dockerfile 的文本文件并复制以下内容。

    # syntax=docker/dockerfile:1
    FROM postgres:latest
    COPY seed.sql /docker-entrypoint-initdb.d/

    此 Dockerfile 将 seed.sql 脚本直接复制到 PostgreSQL 容器的初始化目录中。

  3. 使用 Docker Compose。

    使用 Docker Compose 可以更轻松地管理和部署包含预填充数据库的 PostgreSQL 容器。此 compose.yml 文件定义了一个名为 db 的 Postgres 服务,使用最新的 Postgres 镜像,该镜像设置了一个名为 sampledb 的数据库,以及用户 postgres 和密码 mysecretpassword

    services:
      db:
        build:
          context: .
          dockerfile: Dockerfile
        container_name: my_postgres_db
        environment:
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: mysecretpassword
          POSTGRES_DB: sampledb
        ports:
          - "5432:5432"
        volumes:
          - data_sql:/var/lib/postgresql/data   # Persistent data storage
    
    volumes:
      data_sql:

    它将主机上的端口 5432 映射到容器的 5432,允许你从容器外部访问 Postgres 数据库。它还定义了 data_sql 用于持久化数据库数据,确保在容器停止时数据不会丢失。

    值得注意的是,只有当你需要从非容器化程序连接到数据库时,才需要将端口映射到主机。如果你将连接到数据库的服务容器化,则应该通过自定义桥接网络连接到数据库。

  4. 启动 Compose 服务。

    假设你已将 seed.sql 文件放在与 Dockerfile 相同的目录中,请执行以下命令

    $ docker compose up -d --build
    
  5. 现在是时候验证 users 表是否已填充数据了。

    $ docker exec -it my_postgres_db psql -h localhost -U postgres sampledb
    
    sampledb=# SELECT * FROM users;
      id | name  |       email
    ----+-------+-------------------
       1 | Alpha | alpha@example.com
       2 | Beta  | beta@example.com
       3 | Gamma | gamma@example.com
     (3 rows)
    
    sampledb=#

使用 JavaScript 代码预填充数据库

现在你已经学习了如何使用各种方法(如 SQL 脚本、挂载卷等)预填充数据库,是时候尝试使用 JavaScript 代码来实现它了。

  1. 创建一个 .env 文件,内容如下

    POSTGRES_USER=postgres
    POSTGRES_DB_HOST=localhost
    POSTGRES_DB=sampledb
    POSTGRES_PASSWORD=mysecretpassword
    POSTGRES_PORT=5432
  2. 创建一个名为 seed.js 的新 JavaScript 文件,内容如下

    以下 JavaScript 代码导入了 dotenv 包,该包用于从 .env 文件加载环境变量。.config() 方法读取 .env 文件并将环境变量设置为 process.env 对象的属性。这使得你可以安全地将敏感信息(如数据库凭据)存储在代码之外。

    然后,它从 pg 库创建了一个新的 Pool 实例,该库提供连接池以实现高效的数据库交互。定义了 seedData 函数来执行数据库预填充操作。在脚本末尾调用该函数以启动预填充过程。try...catch...finally 块用于错误处理。

    require('dotenv').config();  // Load environment variables from .env file
    const { Pool } = require('pg');
    
    // Create a new pool using environment variables
    const pool = new Pool({
      user: process.env.POSTGRES_USER,
      host: process.env.POSTGRES_DB_HOST,
      database: process.env.POSTGRES_DB,
      port: process.env.POSTGRES_PORT,
      password: process.env.POSTGRES_PASSWORD,
    });
    
    const seedData = async () => {
      try {
         // Drop the table if it already exists (optional)
         await pool.query(`DROP TABLE IF EXISTS todos;`);
    
         // Create the table with the correct structure
         await pool.query(`
           CREATE TABLE todos (
             id SERIAL PRIMARY KEY,
             task VARCHAR(255) NOT NULL,
             completed BOOLEAN DEFAULT false
               );
         `   );
    
         // Insert seed data
         await pool.query(`
           INSERT INTO todos (task, completed) VALUES
           ('Watch netflix', false),
           ('Finish podcast', false),
           ('Pick up kid', false);
           `);
           console.log('Database seeded successfully!');
         } catch (err) {
           console.error('Error seeding the database', err);
         } finally {
           pool.end();
        }
      };
    
      // Call the seedData function to run the script
      seedData();
  3. 启动预填充过程

    $ node seed.js
    

    你应该看到以下命令

    Database seeded successfully!
  4. 验证数据库是否已正确预填充

    $ docker exec -it postgres psql -h localhost -U postgres sampledb
    
    sampledb=# SELECT * FROM todos;
    id |      task      | completed
    ----+----------------+-----------
    1 | Watch netflix  | f
    2 | Finish podcast | f
    3 | Pick up kid    | f
    (3 rows)  
    

总结

在启动时预填充带模式和数据的数据库对于创建一致且真实的测试环境至关重要,这有助于在开发早期识别问题,并协调前端和后端工作。本指南为你提供了使用各种方法实现预填充的知识和实际步骤,包括 SQL 脚本、Docker 集成和 JavaScript 代码。

页面选项