跳转至

第6章:配置数据库

数据库允许你在你的应用程序中持久保存数据。在本章中,你将学习如何配置你的Vapor应用程序与你选择的数据库集成。

本章以及本书的大部分内容都使用Docker来托管数据库。Docker是一种容器化技术,它允许你在你的机器上运行独立的镜像,而没有虚拟机的开销。你可以旋转不同的数据库,不用担心安装依赖性或数据库之间的相互干扰。

为什么使用数据库?

数据库为存储和检索数据提供了一种可靠的、高性能的手段。如果你的应用程序将信息存储在内存中,那么当你停止应用程序时,它就会丢失。将存储与你的应用程序解耦是一个很好的做法,因为这允许你在多个实例中扩展你的应用程序,所有这些实例都由同一个数据库支持。事实上,大多数托管解决方案都没有持久性文件存储。

选择一个数据库

Vapor有官方的、Swift原生的驱动程序:

  • SQLite
  • MySQL
  • PostgreSQL
  • MongoDB

有两种类型的数据库:关系型,或SQL数据库,以及非关系型,或NoSQL数据库。关系型数据库将其数据存储在具有定义列的结构化表格中。它们在存储和查询数据方面很有效,因为这些数据的结构是预先知道的。你用一种结构化查询语言(SQL)来创建和查询表格,允许你从多个相关的表格中检索数据。例如,如果你在一个表中有一个宠物列表,在另一个表中有一个主人列表,你可以用一个查询来检索宠物列表和它们主人的名字。

虽然关系型数据库适合于刚性结构,但如果你必须改变这种结构,这可能是一个问题。最近,NoSQL数据库作为一种存储大量非结构化数据的方式变得很流行。例如,社交网络可以将设置、图像、位置、状态和指标全部存储在一个文件中。这使其比传统数据库有更大的灵活性。

MySQLPostgreSQL是关系型数据库的例子。MongoDB是一个非关系型数据库的例子。Fluent支持这两种类型的数据库,有不同的底层驱动。但要注意的是,你不能直接用Fluent来充分利用你选择的数据库。Fluent必须支持这两种类型的数据库,并为每个数据库提供同等的功能。不过你可以为某个特定的数据库扩展Fluent的功能,比如说增加对PostGIS的支持。如果需要的话,也很容易进行原始查询。

SQLite

SQLite是一个简单的、基于文件的关系数据库系统。它被设计成嵌入到一个应用程序中,对单进程应用程序(如iOS应用程序)很有用。它依赖于文件锁来保持数据库的完整性,所以它不适合写密集型的应用程序。这也意味着你不能跨服务器使用它。然而,它是一个很好的数据库,适用于测试和原型开发的应用程序。

MySQL

MySQL是另一个开源的关系型数据库,由LAMP网络应用程序栈(LinuxApacheMySQLPHP)普及。它已经成为最受欢迎的数据库,因为它易于使用,并得到大多数云提供商和网站建设者的支持。

PostgreSQL

PostgreSQL--经常被简称为Postgres--是一个开源的关系型数据库系统,专注于可扩展性和标准,是为企业使用设计的。Postgres也有对几何基元的本地支持,如坐标。Fluent支持这些基元,也支持将嵌套类型,如字典,直接保存到Postgres中。

MongoDB

MongoDB是一个流行的开源、基于文档的非关系型数据库,旨在处理大量的非结构化数据,并具有极高的可扩展性。它将数据存储在类似JSON的文件中,采用人类可读的格式,不需要任何特定的结构。

配置Vapor

配置Vapor应用程序以使用数据库的步骤与所有支持的数据库相同,如下所示。

  • Fluent Provider作为一个依赖项添加到项目中。
  • 配置数据库。

本章中的每个数据库配方都以TILApp开始,就像你在第5章"Fluent & Persisting Models"中留下的那样。你还需要安装并运行Docker。访问https://www.docker.com/get-docker,按照说明进行安装。工具箱允许你选择支持哪一个数据库,但你将学习如何手动选择一个不同的数据库。

SQLite

与其他数据库类型不同,SQLite不需要你运行数据库服务器,因为SQLite使用本地文件。打开你项目目录中的Package.swift。用下面的内容替换:

// swift-tools-version:5.2

import PackageDescription

let package = Package(
  name: "TILApp",
  platforms: [
    .macOS(.v10_15)
  ],
  dependencies: [
    .package(
      url: "https://github.com/vapor/vapor.git", 
      from: "4.0.0"),
    .package(
      url: "https://github.com/vapor/fluent.git", 
      from: "4.0.0"),
    // 1
    .package(
      url: "https://github.com/vapor/fluent-sqlite-driver.git", 
      from: "4.0.0")
  ],
  targets: [
    .target(
      name: "App",
      dependencies: [
        .product(name: "Fluent", package: "fluent"),
        // 2
        .product(
          name: "FluentSQLiteDriver", 
          package: "fluent-sqlite-driver"),
        .product(name: "Vapor", package: "vapor")
      ],
      swiftSettings: [
        .unsafeFlags(
          ["-cross-module-optimization"], 
          .when(configuration: .release))
      ]
    ),
    .target(name: "Run", dependencies: [.target(name: "App")]),
    .testTarget(name: "AppTests", dependencies: [
      .target(name: "App"),
      .product(name: "XCTVapor", package: "vapor"),
    ])
  ]
)

下面是这个的作用:

  1. 指定FluentSQLiteDriver作为软件包的依赖。
  2. 指定App目标依赖FluentSQLiteDriver,以确保其链接正确。

像其他数据库一样,数据库配置发生在Sources/App/configure.swift。要切换到SQLite,请将该文件的内容替换为:

import Fluent
// 1
import FluentSQLiteDriver
import Vapor

// configures your application
public func configure(_ app: Application) throws {  
  app.databases.use(.sqlite(.memory), as: .sqlite)

  app.migrations.add(CreateAcronym())

  app.logger.logLevel = .debug

  try app.autoMigrate().wait()

  // register routes
  try routes(app)
}

这些变化是:

  1. 导入FluentSQLiteDriver
  2. 配置应用程序使用具有.sqlite标识符的内存SQLite数据库。

你可以将SQLite配置为使用内存数据库--这意味着应用程序在每次运行时都会创建一个新的数据库实例。该数据库驻留在内存中,它不被持久化到磁盘上,并且在应用程序终止时丢失。这对于测试和原型设计是很有用的。

如果你想用SQLite进行持久化存储,请提供SQLiteDatabase的路径,如下所示:

app.databases.use(.sqlite(.file("db.sqlite")), as: .sqlite)

如果文件不存在,这将在指定路径下创建一个数据库文件。如果该文件存在,Fluent就会使用它。

确保你将部署目标设置为My Mac,然后构建并运行你的应用程序。

寻找控制台中的迁移信息。

img

MySQL

要用MySQL进行测试,在Docker容器中运行MySQL服务器。在终端输入以下命令:

docker run --name mysql \
  -e MYSQL_USER=vapor_username \
  -e MYSQL_PASSWORD=vapor_password \
  -e MYSQL_DATABASE=vapor_database \
  -e MYSQL_RANDOM_ROOT_PASSWORD=yes \
  -p 3306:3306 -d mysql

下面是这个的作用:

  • 运行一个名为mysql的新容器。
  • 通过环境变量指定数据库名称、用户名和密码。
  • 设置MYSQL_RANDOM_ROOT_PASSWORD,将所需的根密码设置为一个随机值。
  • 允许应用程序在其默认端口连接到MySQL服务器:3306.
  • 在后台作为一个守护程序运行服务器。
  • 为这个容器使用名为mysqlDocker镜像。如果该镜像在你的机器上不存在,Docker会自动下载它。

要检查你的数据库是否在运行,在终端输入以下内容,以列出所有活动的容器:

docker ps

img

现在,MySQL正在运行,设置你的Vapor应用程序。打开Package.swift;用下面的内容替换它:

// swift-tools-version:5.2
import PackageDescription

let package = Package(
  name: "TILApp",
  platforms: [
    .macOS(.v10_15)
  ],
  dependencies: [
    .package(
      url: "https://github.com/vapor/vapor.git", 
      from: "4.0.0"),
    .package(
      url: "https://github.com/vapor/fluent.git", 
      from: "4.0.0"),
    // 1
    .package(
      url: "https://github.com/vapor/fluent-mysql-driver.git", 
      from: "4.0.0")
  ],
  targets: [
    .target(
      name: "App",
      dependencies: [
        .product(name: "Fluent", package: "fluent"),
        // 2
        .product(
          name: "FluentMySQLDriver", 
          package: "fluent-mysql-driver"),
        .product(name: "Vapor", package: "vapor")
      ],
      swiftSettings: [
        .unsafeFlags(
          ["-cross-module-optimization"], 
          .when(configuration: .release))
      ]
    ),
    .target(name: "Run", dependencies: [.target(name: "App")]),
    .testTarget(name: "AppTests", dependencies: [
      .target(name: "App"),
      .product(name: "XCTVapor", package: "vapor"),
    ])
  ]
)

下面是这个的作用:

  1. 指定FluentMySQLDriver作为软件包的依赖。
  2. 指定App目标依赖FluentMySQLDriver以确保其链接正确。

接下来,打开configure.swift。要切换到MySQL,将内容替换为以下内容:

import Fluent
// 1
import FluentMySQLDriver
import Vapor

// configures your application
public func configure(_ app: Application) throws {
  // 2
  app.databases.use(.mysql(
    hostname: Environment.get("DATABASE_HOST") ?? "localhost",
    username: Environment.get("DATABASE_USERNAME") 
      ?? "vapor_username",
    password: Environment.get("DATABASE_PASSWORD") 
      ?? "vapor_password",
    database: Environment.get("DATABASE_NAME") 
      ?? "vapor_database",
    tlsConfiguration: .forClient(certificateVerification: .none)
  ), as: .mysql)

  app.migrations.add(CreateAcronym())

  app.logger.logLevel = .debug

  try app.autoMigrate().wait()

  // register routes
  try routes(app)
}

这些变化是:

  1. 导入FluentMySQLDriver
  2. 使用.mysql标识符在应用程序中注册数据库。你使用环境变量提供数据库的凭证。如果环境变量不存在,配置使用你提供给docker的相同的硬编码值。

Note

MySQL默认使用TLS连接。当在Docker中运行时,MySQL会生成一个自签名的证书。你的应用程序不知道这个证书。为了让你的应用程序能够连接,你需要禁用证书验证。你一定不能在生产应用中使用这个。你应该为生产应用提供信任的证书。

确保你将部署目标设置为My Mac,然后构建并运行你的应用程序。

寻找控制台中的迁移信息。

img

MongoDB

要用MongoDB进行测试,在Docker容器中运行MongoDB服务器。在终端输入以下命令:

docker run --name mongo \
  -e MONGO_INITDB_DATABASE=vapor \
  -p 27017:27017 -d mongo

下面是这个的作用:

  • 运行一个名为mongo的新容器。
  • 通过一个环境变量指定数据库名称。
  • 允许应用程序连接到MongoDB服务器的默认端口:27017.
  • 在后台作为一个守护程序运行服务器。
  • 为这个容器使用名为mongoDocker镜像。如果该镜像在你的机器上不存在,Docker会自动下载它。

要检查你的数据库是否在运行,在终端输入以下内容,以列出所有活动的容器:

docker ps

img

现在MongoDB正在运行,设置你的Vapor应用程序。打开Package.swift;将其内容改为以下内容:

// swift-tools-version:5.2
import PackageDescription

let package = Package(
  name: "TILApp",
  platforms: [
    .macOS(.v10_15)
  ],
  dependencies: [
    .package(
      url: "https://github.com/vapor/vapor.git", 
      from: "4.0.0"),
    .package(
      url: "https://github.com/vapor/fluent.git", 
      from: "4.0.0"),
    // 1
    .package(
      url: "https://github.com/vapor/fluent-mongo-driver.git", 
      from: "1.0.0")
  ],
  targets: [
    .target(
      name: "App",
      dependencies: [
        .product(name: "Fluent", package: "fluent"),
        // 2
        .product(
          name: "FluentMongoDriver", 
          package: "fluent-mongo-driver"),
        .product(name: "Vapor", package: "vapor")
      ],
      swiftSettings: [
        .unsafeFlags(
          ["-cross-module-optimization"], 
          .when(configuration: .release))
      ]
    ),
    .target(name: "Run", dependencies: [.target(name: "App")]),
    .testTarget(name: "AppTests", dependencies: [
      .target(name: "App"),
      .product(name: "XCTVapor", package: "vapor"),
    ])
  ]
)

下面是这个的作用:

  1. 指定FluentMongoDriver作为软件包的依赖关系。
  2. 指定App目标依赖FluentMongoDriver以确保其正确链接。

接下来,打开configure.swift。要切换到MongoDB,请将内容替换为以下内容:

import Fluent
// 1
import FluentMongoDriver
import Vapor

// configures your application
public func configure(_ app: Application) throws {
  // 2
  try app.databases.use(.mongo(
    connectionString: "mongodb://localhost:27017/vapor"), 
    as: .mongo)

  app.migrations.add(CreateAcronym())

  app.logger.logLevel = .debug

  try app.autoMigrate().wait()

  // register routes
  try routes(app)
}

这些变化是:

  1. 导入FluentMongoDriver
  2. 使用.mongo标识符在应用程序中注册数据库。MongoDB使用一个连接URL,如图所示。该URL指定了主机--在这里是localhost--端口和数据库的路径。该路径与提供给Docker的数据库名称相同。默认情况下,MongoDB不需要认证,但如果需要,你可以在这里提供认证。

确保你将部署目标设置为My Mac,然后构建并运行你的应用程序。

寻找控制台中的迁移信息。

img

PostgreSQL

你创建的第5章"Fluent与持久化模型 "中的Vapor应用已经使用了PostgreSQL。记住,你在Docker中用下面的命令在终端创建了一个PostgreSQL数据库:

docker run --name postgres \
  -e POSTGRES_DB=vapor_database \
  -e POSTGRES_USER=vapor_username \
  -e POSTGRES_PASSWORD=vapor_password \
  -p 5432:5432 -d postgres

以下是这样做的:

  • 运行一个名为postgres的新容器。
  • 通过环境变量指定数据库名称、用户名和密码。
  • 允许应用程序连接到Postgres服务器的默认端口:5432.
  • 在后台作为一个守护程序运行服务器。
  • 为这个容器使用名为postgresDocker镜像。如果你的机器上没有这个镜像,Docker会自动下载它。

要检查你的数据库是否在运行,在终端输入以下内容,以列出所有活动的容器:

docker ps

img

要了解你的Vapor应用程序如何使用PostgreSQL。打开Package.swift。它看起来将类似于下面:

// swift-tools-version:5.2
import PackageDescription

let package = Package(
  name: "TILApp",
  platforms: [
    .macOS(.v10_15)
  ],
  dependencies: [
    // 💧 A server-side Swift web framework.
    .package(
      url: "https://github.com/vapor/vapor.git", 
      from: "4.0.0"),
    .package(
      url: "https://github.com/vapor/fluent.git", 
      from: "4.0.0"),
    .package(
      url: 
        "https://github.com/vapor/fluent-postgres-driver.git",
      from: "2.0.0")
  ],
  targets: [
    .target(
      name: "App",
      dependencies: [
        .product(name: "Fluent", package: "fluent"),
        .product(
          name: "FluentPostgresDriver", 
          package: "fluent-postgres-driver"),
        .product(name: "Vapor", package: "vapor")
      ],
      swiftSettings: [
        .unsafeFlags(
          ["-cross-module-optimization"], 
          .when(configuration: .release))
      ]
    ),
    .target(name: "Run", dependencies: [.target(name: "App")]),
    .testTarget(name: "AppTests", dependencies: [
      .target(name: "App"),
      .product(name: "XCTVapor", package: "vapor"),
    ])
  ]
)

你可以看到你的应用程序依赖于FluentPostgresDriver。数据库配置发生在configure.swift中,就像所有其他的数据库类型一样。你的configure.swift应该包含以下内容:

import Fluent
// 1
import FluentPostgresDriver
import Vapor

// configures your application
public func configure(_ app: Application) throws {
  // 2
  app.databases.use(.postgres(
    hostname: Environment.get("DATABASE_HOST")
      ?? "localhost",
    username: Environment.get("DATABASE_USERNAME")
      ?? "vapor_username",
    password: Environment.get("DATABASE_PASSWORD")
      ?? "vapor_password",
    database: Environment.get("DATABASE_NAME") 
      ?? "vapor_database"
  ), as: .psql)

  // 3
  app.migrations.add(CreateAcronym())

  app.logger.logLevel = .debug

  // 4
  try app.autoMigrate().wait()

  // register routes
  try routes(app)
}

下面是这个的作用:

  1. 导入FluentPostgresDriver
  2. .psql标识符配置PostgreSQL数据库。这要么使用作为环境变量传递的证书,要么使用与传递给Docker的证书一致的硬编码证书。
  3. CreateAcronym添加到应用程序的迁移列表中。
  4. 在应用启动时自动运行迁移程序。

当你第一次运行你的应用程序时,你会看到迁移的运行:

img

接下来去哪?

在本章中,你已经学会了如何为你的应用程序配置一个数据库。下一章将介绍CRUD操作,以便你可以创建、检索、更新和删除你的缩写。