配置

配置

本指南是配置 Lucid 的参考。你将学会如何:

  • 定义默认连接并注册命名连接
  • 配置每种支持的数据库驱动
  • 对托管数据库使用连接字符串和 SSL
  • 查询多个数据库并使用读/写副本
  • 为生产负载调整连接池
  • 配置迁移、种子和 schema 生成
  • 保护表免受 db:wipe 影响并启用调试输出

概述

Lucid 将其数据库配置存储在 config/database.ts 中。该文件导出 defineConfig 的结果,描述了默认连接以及你的应用可以使用的每个命名连接。

连接在应用启动时注册,但 Lucid 在你执行第一个查询时惰性打开它们。这保持了低启动时间,并允许应用定义仅由后台任务、报表或特定模型使用的连接。AdonisJS 还会在应用关闭时自动关闭每个已注册的连接,因此优雅的清理已为你处理。

配置文件

Lucid 配置有两个必需的顶级属性。connection 是默认连接的名称,connections 是命名连接配置的映射。

config/database.ts
import env from '#start/env'
import { defineConfig } from '@adonisjs/lucid'
const dbConfig = defineConfig({
connection: 'postgres',
connections: {
postgres: {
client: 'pg',
connection: {
host: env.get('DB_HOST'),
port: env.get('DB_PORT'),
user: env.get('DB_USER'),
password: env.get('DB_PASSWORD'),
database: env.get('DB_DATABASE'),
},
migrations: {
naturalSort: true,
paths: ['database/migrations'],
},
},
},
})
export default dbConfig

顶级配置接受以下选项。

connection

默认连接的名称。当你不显式选择连接时,Lucid 使用此连接。该值必须与 connections 下的某个键匹配。

connections

命名连接配置的映射。每个条目描述一个 client、一个 connection 对象或连接字符串,以及可选的共享设置,如 migrationsseederspoolschemaGeneration

prettyPrintDebugQueries

当设置为 true 时,Lucid 会为 db:query 事件附加一个监听器,将每个执行的 SQL 语句格式化打印到控制台。详见调试输出

驱动

每个连接必须指定一个与支持的数据库驱动匹配的 client 值。Lucid 在你使用 --db 运行 configure 命令时会安装所需的驱动包,你也可以手动安装。

数据库Client
SQLitebetter-sqlite3better-sqlite3
SQLitesqlite3sqlite3
LibSQLlibsql@libsql/sqlite3
MySQLmysql2mysql2
PostgreSQLpgpg
MSSQLmssqltedious

对于使用网络数据库的连接(MySQL、PostgreSQL、MSSQL),connection 对象接受一组通用字段:hostportuserpassworddatabase。每个驱动在此基础上添加自己的选项,详见以下各节。

SQLite

使用 SQLite 进行本地开发、测试或不需要独立数据库服务器的轻量级生产负载。SQLite 将数据存储在磁盘上的单个文件中。

config/database.ts
import env from '#start/env'
import { defineConfig } from '@adonisjs/lucid'
const dbConfig = defineConfig({
connection: 'sqlite',
connections: {
sqlite: {
client: 'better-sqlite3',
connection: {
filename: env.get('DB_DATABASE', 'tmp/db.sqlite3'),
},
useNullAsDefault: true,
migrations: {
naturalSort: true,
paths: ['database/migrations'],
},
},
},
})
export default dbConfig

连接对象接受以下字段。

filename

SQLite 数据库文件的路径。只要父目录存在,Lucid 会在首次运行查询时自动创建文件。AdonisJS starter kit 附带一个 tmp/ 目录,新项目默认使用此目录。

flags

驱动特定的标志数组。支持的取值请参考 sqlite3better-sqlite3 的驱动文档。

mode

可选的驱动模式,如只读。支持的取值请参考驱动文档。

SQLite 不支持读/写副本。replicas 属性在 SQLite 连接上不可用。

LibSQL

LibSQL 是 SQLite 的即插即用替代品,通常与 Turso 一起用于边缘部署。配置选项与 SQLite 相同。

config/database.ts
import env from '#start/env'
import { defineConfig } from '@adonisjs/lucid'
const dbConfig = defineConfig({
connection: 'libsql',
connections: {
libsql: {
client: 'libsql',
connection: {
filename: env.get('DB_DATABASE', 'tmp/db.sqlite3'),
},
useNullAsDefault: true,
migrations: {
naturalSort: true,
paths: ['database/migrations'],
},
},
},
})
export default dbConfig

与 SQLite 一样,LibSQL 连接不支持通过 Lucid 的 replicas 属性使用读/写副本。

MySQL

使用 mysql2 客户端连接 MySQL、MariaDB 或 MySQL 兼容的服务。

config/database.ts
import env from '#start/env'
import { defineConfig } from '@adonisjs/lucid'
const dbConfig = defineConfig({
connection: 'mysql',
connections: {
mysql: {
client: 'mysql2',
connection: {
host: env.get('DB_HOST'),
port: env.get('DB_PORT'),
user: env.get('DB_USER'),
password: env.get('DB_PASSWORD'),
database: env.get('DB_DATABASE'),
},
migrations: {
naturalSort: true,
paths: ['database/migrations'],
},
},
},
})
export default dbConfig

连接对象接受通用的 hostportuserpassworddatabase 字段,以及以下驱动特定的选项。

socketPath

用于本地 MySQL 连接的 Unix 套接字路径。提供后,驱动将忽略 hostport

charset

连接使用的字符集。在现代 MySQL 设置中默认为 utf8mb4_unicode_ci

timezone

驱动在读写 DATETIMETIMESTAMP 列时使用的时区。设置为 'Z' 强制使用 UTC,这对大多数应用来说是最安全的默认值。

ssl

需要加密传输的连接的 SSL 配置。常见格式请参见 SSL 和生产注意事项

dateStrings

设置为 true 时,驱动将日期和 datetime 列作为字符串返回,而非 JavaScript Date 对象。当 Lucid 模型通过 Luxon 执行自己的日期解析时很有用。

decimalNumbers

设置为 truemysql2 特定选项)时,驱动将 DECIMALNEWDECIMAL 列转换为 JavaScript 数字而非字符串。仅在精度损失可接受时启用。

PostgreSQL

使用 pg 客户端连接 PostgreSQL 及兼容服务。

config/database.ts
import env from '#start/env'
import { defineConfig } from '@adonisjs/lucid'
const dbConfig = defineConfig({
connection: 'postgres',
connections: {
postgres: {
client: 'pg',
connection: {
host: env.get('DB_HOST'),
port: env.get('DB_PORT'),
user: env.get('DB_USER'),
password: env.get('DB_PASSWORD'),
database: env.get('DB_DATABASE'),
},
migrations: {
naturalSort: true,
paths: ['database/migrations'],
},
},
},
})
export default dbConfig

连接接受标准字段以及以下 PostgreSQL 特定选项。

ssl

需要加密传输的连接的 SSL 配置。设置为 true 进行简单加密连接,或传入一个具有 Node.js tls.ConnectionOptions 形状的对象来提供证书和验证规则。参见 SSL 和生产注意事项

connectionString

完整的 PostgreSQL 连接 URL。在 connection 对象内设置时,驱动解析 URL 并将其与其他字段合并。这是将 URL 作为顶级 connection 值的替代方案。

PostgreSQL 配置还在连接级别、connection 对象之外暴露了两个选项。

searchPath

要在连接的 search_path 上设置的 PostgreSQL schema 数组。当你的应用在多个 schema 中存储表时使用,例如 ['public', 'tenant_1']

returning

Knex 为 insert 和 update 查询添加的 RETURNING 子句的默认值。默认为 id。仅当你需要不同的默认列来返回行时覆盖此设置。

MSSQL

使用 mssql 客户端连接 Microsoft SQL Server 和 Azure SQL。

config/database.ts
import env from '#start/env'
import { defineConfig } from '@adonisjs/lucid'
const dbConfig = defineConfig({
connection: 'mssql',
connections: {
mssql: {
client: 'mssql',
connection: {
server: env.get('DB_HOST'),
port: env.get('DB_PORT'),
user: env.get('DB_USER'),
password: env.get('DB_PASSWORD'),
database: env.get('DB_DATABASE'),
options: {
encrypt: true,
},
},
migrations: {
naturalSort: true,
paths: ['database/migrations'],
},
},
},
})
export default dbConfig

注意 MSSQL 使用 server 而非 host 表示主机名。连接接受以下驱动特定选项。

server

SQL Server 实例的主机名。必需。

domain

Windows 身份验证的域名。标准用户名和密码身份验证时无需设置。

connectionTimeout

建立新连接时等待的毫秒数,超时后放弃。默认为 15000

requestTimeout

等待单个查询完成的毫秒数,超时后中止。默认为 15000

options.encrypt

是否对连接使用 TLS。Azure SQL 和大多数托管 SQL Server 提供商要求将其设置为 true

options.trustServerCertificate

设置为 true 时,驱动跳过 TLS 证书验证。仅用于针对自签名证书的开发环境,切勿在生产环境中使用。

options.isolationLevel

事务的默认隔离级别。支持的值:READ_UNCOMMITTEDREAD_COMMITTEDREPEATABLE_READSERIALIZABLESNAPSHOT

options.instanceName

当同一主机上运行多个实例时,要连接的 SQL Server 命名实例。

连接字符串

当托管服务提供商将凭据作为单个 URL 暴露时,连接字符串很方便。直接将 URL 作为 connection 值传入。

config/database.ts
import env from '#start/env'
import { defineConfig } from '@adonisjs/lucid'
const dbConfig = defineConfig({
connection: 'postgres',
connections: {
postgres: {
client: 'pg',
connection: env.get('DATABASE_URL'),
migrations: {
naturalSort: true,
paths: ['database/migrations'],
},
},
},
})
export default dbConfig
.env
DATABASE_URL=postgres://username:password@localhost:5432/database_name

保持一个凭据真相源。如果生产环境暴露 DATABASE_URL,则在两个环境中使用连接字符串,而不是将相同的值拆分为单独的 DB_HOSTDB_PORTDB_USER 变量。

对于 PostgreSQL,你还可以使用 connectionString 字段在连接对象内传入 URL,当你需要将其与 ssl 等额外选项合并时很有用。

config/database.ts
const dbConfig = defineConfig({
connection: 'postgres',
connections: {
postgres: {
client: 'pg',
connection: {
connectionString: env.get('DATABASE_URL'),
ssl: { rejectUnauthorized: false },
},
},
},
})

SSL 和生产注意事项

大多数托管数据库要求生产连接使用 TLS。不同驱动的配置略有不同。

对于 PostgreSQL,传入一个具有 Node.js tls.ConnectionOptions 形状的 ssl 对象。使用 Let's Encrypt 样式证书的提供商通常可以使用默认设置。自签名证书需要 rejectUnauthorized: false 或显式的 ca 链。

config/database.ts
connection: {
host: env.get('DB_HOST'),
port: env.get('DB_PORT'),
user: env.get('DB_USER'),
password: env.get('DB_PASSWORD'),
database: env.get('DB_DATABASE'),
ssl: {
rejectUnauthorized: true,
ca: env.get('DB_CA_CERT'),
},
}

对于 MySQL,传入一个相同形状的 ssl 对象,或使用 { rejectUnauthorized: false } 为不受信任证书的提供商跳过验证。

config/database.ts
connection: {
host: env.get('DB_HOST'),
user: env.get('DB_USER'),
password: env.get('DB_PASSWORD'),
database: env.get('DB_DATABASE'),
ssl: {
rejectUnauthorized: true,
},
}

对于 MSSQL,TLS 在 options.encrypt 中配置。对于 Azure SQL 和大多数托管提供商,将其设置为 true。连接到本地开发 SQL Server 实例时,你可能还需要 options.trustServerCertificate: true

config/database.ts
connection: {
server: env.get('DB_HOST'),
user: env.get('DB_USER'),
password: env.get('DB_PASSWORD'),
database: env.get('DB_DATABASE'),
options: {
encrypt: true,
trustServerCertificate: false,
},
}

还有两个值得强调的与生产相关的选项:

  • 对于 PostgreSQL,当你的应用使用非默认 schema 时设置 searchPath。没有它,Lucid(和 Knex)仅针对 public 解析未限定的表名。
  • 对于 MySQL,设置 timezone: 'Z',以便驱动无论数据库服务器的本地时区如何,都以 UTC 读写时间戳。

多连接

当一个应用需要查询不同数据库时,定义多个命名连接。顶级 connection 值仍然是默认连接。

config/database.ts
import env from '#start/env'
import { defineConfig } from '@adonisjs/lucid'
const dbConfig = defineConfig({
connection: 'primary',
connections: {
primary: {
client: 'pg',
connection: env.get('PRIMARY_DATABASE_URL'),
migrations: {
paths: ['database/migrations'],
},
},
analytics: {
client: 'pg',
connection: env.get('ANALYTICS_DATABASE_URL'),
migrations: {
paths: ['database/analytics_migrations'],
},
},
},
})
export default dbConfig

使用 db.connection('name') 从应用代码中查询命名连接。

app/services/reports_service.ts
import db from '@adonisjs/lucid/services/db'
export async function getSignupTotals() {
return db
.connection('analytics')
.from('daily_signups')
.select('date', 'total')
.orderBy('date', 'desc')
}

对于始终位于非默认连接上的模型,在模型上设置 static connection,以便每个查询和关联关系都使用正确的数据库。

app/models/daily_signup.ts
import { DailySignupsSchema } from '#database/schema'
export default class DailySignup extends DailySignupsSchema {
static connection = 'analytics'
}

每个连接都有自己独立的迁移目录,如上面 migrations.paths 示例所示。Lucid 的迁移命令接受 --connection 标志,以便你可以指定特定连接。更多信息请参见迁移指南

读/写副本

读/写副本允许你将读查询发送到一个或多个读节点,将写查询发送到写节点。Lucid 以轮询顺序选择读节点,但它不会为你复制数据。数据库复制必须在 Lucid 之外配置。

config/database.ts
import env from '#start/env'
import { defineConfig } from '@adonisjs/lucid'
const dbConfig = defineConfig({
connection: 'postgres',
connections: {
postgres: {
client: 'pg',
connection: {
user: env.get('DB_USER'),
password: env.get('DB_PASSWORD'),
database: env.get('DB_DATABASE'),
},
replicas: {
read: {
connection: [
{ host: env.get('DB_READ_HOST_1'), port: env.get('DB_PORT') },
{ host: env.get('DB_READ_HOST_2'), port: env.get('DB_PORT') },
],
},
write: {
connection: {
host: env.get('DB_WRITE_HOST'),
port: env.get('DB_PORT'),
},
},
},
migrations: {
naturalSort: true,
paths: ['database/migrations'],
},
},
},
})
export default dbConfig

replicas 属性在 MySQL、PostgreSQL 和 MSSQL 连接上可用。SQLite 和 LibSQL 不支持副本。

每个副本块接受与主连接相同的连接形状,外加自己的可选 pool 块,以便读和写池可以分别调整。

当工作流需要特定模式时,显式选择读或写客户端。

app/services/posts_service.ts
import db from '@adonisjs/lucid/services/db'
export async function listPublishedPosts() {
return db
.connection('postgres', { mode: 'read' })
.from('posts')
.where('is_published', true)
}
export async function publishPost(id: number) {
return db
.connection('postgres', { mode: 'write' })
.from('posts')
.where('id', id)
.update({ is_published: true })
}

副本延迟可能会导致新写入的行在短时间内无法在读副本上使用。

对于写入后立即读取行的工作流,选择写连接,以便查询针对最新的主节点运行。

连接池

连接池维护一组有限数量的打开数据库连接,并在查询之间重用它们。更大的池并不自动更好:过多的并发数据库连接可能会降低数据库性能,并使共享同一数据库的其他应用饥饿。

config/database.ts
import env from '#start/env'
import { defineConfig } from '@adonisjs/lucid'
const dbConfig = defineConfig({
connection: 'postgres',
connections: {
postgres: {
client: 'pg',
connection: env.get('DATABASE_URL'),
pool: {
min: 2,
max: 10,
acquireTimeoutMillis: 60_000,
afterCreate: (conn, done) => {
conn.query(`SET timezone='UTC';`, (err: Error) => done(err, conn))
},
},
},
},
})
export default dbConfig

根据数据库服务器容量和应用进程数量设置 max。如果你运行八个 Node.js 进程,每个进程 max: 10,则应用最多可以打开 80 个数据库连接。

pool 块接受以下选项。

min

池中保持打开的最小连接数。默认为 2

max

池在排队新请求之前将打开的最大连接数。默认为 10

acquireTimeoutMillis

从池中等待连接的超时毫秒数,超时后抛出超时错误。默认为 60000

createTimeoutMillis

创建新连接时等待的毫秒数,超时后视为失败。默认为 30000

idleTimeoutMillis

空闲连接在池中保持的毫秒数,超时后关闭。默认为 30000

createRetryIntervalMillis

创建新连接失败后重试之间等待的毫秒数。默认为 200

reapIntervalMillis

池扫描已超过 idleTimeoutMillis 的空闲连接的频率。默认为 1000

propagateCreateError

设置为 true 时,失败的连接创建错误会抛出给触发创建的调用者。false(默认)时,错误被吞掉,池继续重试。

afterCreate

每个新连接创建后调用的回调。用于运行每个连接的设置,如 SET timezoneSET search_pathPRAGMA 语句。回调接收原始驱动连接和一个必须调用的 done 回调来表示完成。

log

自定义日志回调。由底层池库调用,传入信息性消息。

validate

当连接从池中取出时调用的自定义验证回调。返回 false 会导致连接被销毁并替换。

共享连接选项

本节的选项适用于每种驱动,与 clientconnectionpool 并列在连接上。

useNullAsDefault

当为 true 时,Knex 在插入期间对缺失值使用 NULL 而非数据库的默认值。SQLite 需要此选项以避免在插入未设置所有列的行时出现警告,在其他地方保留无害。

asyncStackTraces

当为 true 时,Knex 为每个查询捕获原始调用点,并将其包含在错误中。这使得在开发期间追踪失败的查询回其源头变得更加容易。该功能有少量运行时开销,因此仅在开发环境中启用。

config/database.ts
{
client: 'pg',
connection: env.get('DATABASE_URL'),
asyncStackTraces: app.inDev,
}

debug

当为 true 时,Knex 的内置查询日志通过 AdonisJS 日志器在 debug 级别路由,因此执行的查询与应用的其余部分一起出现在日志输出中。对于更丰富的信息,如绑定、持续时间和连接名称,请改为订阅 db:query 事件。参见调试输出

迁移配置

每个连接都有自己的 migrations 块。Lucid 的迁移命令读取此块以发现迁移文件、应用它们并跟踪状态。

config/database.ts
{
client: 'pg',
connection: env.get('DATABASE_URL'),
migrations: {
naturalSort: true,
paths: ['database/migrations'],
tableName: 'adonis_schema',
disableTransactions: false,
disableRollbacksInProduction: true,
},
}

paths

要扫描迁移文件的目录数组。在这些目录中找到的每个 .ts.js 文件都按排序顺序加载和执行。默认为 ['database/migrations']

naturalSort

当为 true 时,迁移文件使用自然排序顺序(因此 10_...2_... 之后)。用于时间戳前缀的文件名。默认为 false

tableName

Lucid 用于跟踪哪些迁移已运行的表名。默认为 adonis_schema。仅在与已使用不同跟踪表的现有数据库集成时更改此设置。

disableTransactions

当为 true 时,Lucid 跳过将每个迁移文件包装在事务中。默认情况下,每个迁移在自己的事务中运行,以便部分失败可以干净地回滚。仅当迁移使用的语句无法在事务中运行时禁用此选项,例如某些 PostgreSQL DDL 操作。

disableRollbacksInProduction

当为 true 时,migration:rollbackmigration:resetmigration:refresh 命令拒绝在生产环境中运行。因为回滚操作通常是破坏性的(删除表、移除列),在生产中禁用它们可以防止意外的数据丢失。

种子配置

每个连接还接受 db:seed 命令的 seeders 块。

config/database.ts
{
client: 'pg',
connection: env.get('DATABASE_URL'),
seeders: {
paths: ['database/seeders'],
naturalSort: true,
},
}

paths

要扫描 seeder 文件的目录数组。默认为 ['database/seeders']

naturalSort

当为 true 时,seeder 文件使用自然排序顺序。默认为 false

Schema 生成配置

Lucid 在每次迁移运行后自动重新生成 database/schema.tsschemaGeneration 块控制此行为。

config/database.ts
{
client: 'pg',
connection: env.get('DATABASE_URL'),
schemaGeneration: {
enabled: true,
outputPath: 'database/schema.ts',
excludeTables: ['knex_migrations', 'adonis_schema_versions'],
rulesPaths: ['database/schema_rules.ts'],
},
}

enabled

当为 false 时,Lucid 跳过 schema:generate 命令和迁移后运行的自动重新生成。当你想要手动提交 database/schema.ts 而非在每次迁移时重新生成时使用。默认为 true

outputPath

生成的 schema 文件的写入路径。默认为 database/schema.ts

excludeTables

Lucid 在生成 schema 类时应跳过的表名数组。适用于你的应用不通过 Lucid 模型查询的第三方表。

rulesPaths

schema 规则文件的路径数组。这些文件可以按表或按列覆盖类型映射、列名和其他生成器行为。参见 schema 类指南了解规则参考。

保护表免受 db 影响

db:wipe 命令删除数据库中的所有表。对于混合了 Lucid 管理的表和由其他工具维护的表(如 PostGIS 或消息队列)的工作流,设置 wipe.ignoreTables 以保持这些表不变。

config/database.ts
{
client: 'pg',
connection: env.get('DATABASE_URL'),
wipe: {
ignoreTables: ['spatial_ref_sys', 'pgboss_jobs'],
},
}

PostgreSQL 连接默认已从 wipe 操作中排除 spatial_ref_sys。根据你的设置需要在此添加其他表。

调试输出

Lucid 为每个执行的查询发出 db:query 事件。订阅此事件是在任何环境中记录、分析或检查 SQL 的推荐方式。

start/events.ts
import emitter from '@adonisjs/core/services/emitter'
import logger from '@adonisjs/core/services/logger'
emitter.on('db:query', (query) => {
logger.debug({ sql: query.sql, bindings: query.bindings, duration: query.duration })
})

对于本地开发,Lucid 附带了一个内置的格式化打印器。通过在配置的顶级设置 prettyPrintDebugQueries 来启用它。

config/database.ts
import app from '@adonisjs/core/services/app'
import { defineConfig } from '@adonisjs/lucid'
const dbConfig = defineConfig({
connection: 'postgres',
prettyPrintDebugQueries: app.inDev,
connections: {
postgres: {
client: 'pg',
connection: env.get('DATABASE_URL'),
},
},
})

启用后,每个查询都会打印到终端,带有颜色、计时和已插值的绑定以便阅读。

第三个选项是连接级别的 debug: true 标志,它通过 AdonisJS 日志器在 debug 级别路由 Knex 的内置查询日志。Lucid 在通过此方式使用时还会发出一次性通知,推荐使用 db:query 事件进行更丰富的日志记录。当你想让查询输出通过现有的日志器管道流动而无需编写自定义事件监听器时,该标志仍然有用。

后续步骤

  • 数据库服务指南了解 db 服务入口点、运行时连接选择和管理器 API。
  • 迁移指南了解迁移文件结构、命令和多连接工作流。
  • 事务指南了解隔离级别、托管和手动事务 API 以及跨模型事务。