Table 构建器
本指南是在 createTable 和 alterTable 回调中使用的 table 构建器 API 的参考。你将学会如何:
- 添加每种支持类型的列
- 应用列修饰符,如
notNullable、defaultTo、unsigned和索引 - 定义外键,包括级联规则和可延迟约束
- 添加 check 约束以在数据库层进行值验证
- 在表级别创建复合索引和约束
- 修改、删除和重命名列
- 应用表级选项,如引擎和字符集
概述
Table 构建器是在 schema 构建器的 createTable 或 alterTable 回调中定义列级和表级结构的 API。
import { BaseSchema } from '@adonisjs/lucid/schema'
export default class extends BaseSchema {
async up() {
this.schema.createTable('posts', (table) => {
table.increments('id')
table.string('title').notNullable()
table.text('body')
table.integer('user_id').unsigned().references('users.id').onDelete('CASCADE')
table.timestamps(true, true)
})
}
}
有关 schema 本身的操作(createTable、alterTable、dropTable 等),请参见 schema 构建器参考。有关迁移类相关内容如 this.defer、this.now() 和事务控制,请参见迁移介绍。
列类型
increments
自增整数列。默认标记为主键。
table.increments('id')
table.increments('id', { primaryKey: false }) // 跳过自动主键
PostgreSQL 使用 serial;MySQL 使用 int unsigned auto_increment;Redshift 使用 integer identity (1,1)。
bigIncrements
自增 bigint 列。默认标记为主键。
table.bigIncrements('id')
integer
标准整数列。
table.integer('view_count')
tinyint、smallint、mediumint、bigInteger
更小和更大的整数类型。bigInteger 在 PostgreSQL 和 MySQL 上是 bigint;在没有原生 bigint 的方言上回退为普通整数。bigint 是 bigInteger 的别名。
table.tinyint('flag_byte')
table.smallint('priority')
table.mediumint('view_bucket') // 仅限 MySQL
table.bigInteger('snowflake_id')
Bigint 值在查询结果中以字符串形式返回,以避免 JavaScript 精度丢失。
float
浮点列,可选精度(默认 8)和小数位(默认 2)。
table.float('rating')
table.float('price', 8, 2)
double
双精度浮点列。与 float 相同的精度和小数位参数。
table.double('balance', 14, 4)
decimal
固定精度小数列。将 null 作为精度传入以允许任意精度(PostgreSQL、SQLite、Oracle)。
table.decimal('price')
table.decimal('price', 8, 2)
table.decimal('amount', null) // 任意精度
boolean
布尔列。许多方言将布尔值表示为 0/1 并以此形式返回。
table.boolean('is_published')
string
可变长度字符串列,可选长度(默认 255)。
table.string('title')
table.string('title', 100)
text
长文本列。在 MySQL 上传入 'mediumtext' 或 'longtext' 作为第二个参数;在其他方言上被忽略。
table.text('body')
table.text('body', 'longtext')
date
日期列(无时间部分)。
table.date('dob')
time
时间列(无日期)。MySQL 接受精度选项。
table.time('starts_at')
table.time('starts_at', { precision: 6 })
dateTime
DateTime 列,可选时区和精度。dateTime 和 datetime 是别名。
table.dateTime('starts_at', { useTz: true })
table.dateTime('starts_at', { precision: 6 }).defaultTo(this.now(6))
useTz: true 在 PostgreSQL 上生成 timestamptz,在 MSSQL 上生成 DATETIME2。
timestamp
时间戳列。与 dateTime 相同的 options 对象。
table.timestamp('created_at')
table.timestamp('created_at', { useTz: true })
table.timestamp('created_at', { precision: 6 })
timestamps
便捷方法,添加 created_at 和 updated_at 列。签名为 timestamps(useTimestamps, defaultToNow)。
table.timestamps() // DATETIME 列,无默认值
table.timestamps(true) // TIMESTAMP 列,无默认值
table.timestamps(true, true) // TIMESTAMP 列,默认 CURRENT_TIMESTAMP
对于需要索引、自定义精度或时区感知列的应用,优先使用两个 table.timestamp(...) 调用而非 timestamps。timestamps 快捷方式返回 void,因此你无法在它创建的列上链式调用修饰符。
binary
二进制 blob 列,可选长度参数(仅限 MySQL)。
table.binary('document')
table.binary('document', 1024)
uuid
UUID 列。在 PostgreSQL 上使用原生 uuid 类型,在其他地方使用 char(36)。
table.uuid('id').primary().defaultTo(this.raw('gen_random_uuid()'))
在较旧的 PostgreSQL 版本上,在使用 uuid 列之前,在单独的迁移中安装 uuid-ossp 扩展:
this.schema.raw('CREATE EXTENSION IF NOT EXISTS "uuid-ossp"')
json
JSON 列。在 PostgreSQL、MySQL 和 SQLite 上使用原生 json 类型,在不支持 JSON 的方言上回退为 text 列。
table.json('settings')
jsonb
以二进制表示存储的 JSON 列,支持索引访问。仅限 PostgreSQL;在其他地方回退为 json。
table.jsonb('preferences')
enum / enu
枚举列。传入列名、允许值数组和 options 对象。
table.enu('status', ['draft', 'published', 'archived'])
当你使用 useNative 并提供唯一的 enumName 时,PostgreSQL 支持原生枚举类型。当类型已存在于数据库中时,设置 existingType: true。
table.enu('status', ['draft', 'published', 'archived'], {
useNative: true,
enumName: 'post_status',
existingType: false,
schemaName: 'public',
})
当删除使用原生枚举类型的表时,同时删除该类型以避免留下孤儿:
this.schema.raw('DROP TYPE IF EXISTS "post_status"')
this.schema.dropTable('posts')
enu 是较旧的名称;enum 是后来添加的别名,因为 enum 在某些构建设置中是保留关键字。
geometry、geography、point
空间列。在 PostgreSQL(配合 PostGIS)、MySQL 和其他具有空间支持的方言上可用。
table.geometry('shape')
table.geography('coverage_area')
table.point('location')
specificType
当构建器未原生暴露该类型时,使用原始类型字符串定义列。
table.specificType('mac_address', 'macaddr')
table.specificType('tags', 'text[]') // PostgreSQL 数组
列修饰符
以下方法可在任何列类型方法的结果上链式调用。它们修改正在创建(或修改)的列。
defaultTo
为插入设置默认值。
table.boolean('is_published').defaultTo(false)
table.timestamp('created_at').defaultTo(this.now())
table.uuid('id').defaultTo(this.raw('gen_random_uuid()'))
在 MSSQL 上,传入 constraintName 选项以控制生成的默认约束的名称:
table.boolean('is_published').defaultTo(false, { constraintName: 'df_posts_is_published' })
notNullable 和 nullable
将列标记为 NOT NULL 或 NULL。
table.string('email').notNullable()
table.text('bio').nullable()
在修改现有列时,优先使用 setNullable 和 dropNullable(如下所述),使约束变更更加明确。
unsigned
将数值列标记为无符号。在 PostgreSQL 上无效,因为它不支持无符号整数。
table.integer('user_id').unsigned()
primary
将列标记为主键。传入可选的 options 对象,包含 constraintName 和 deferrable。
table.uuid('id').primary()
table.uuid('id').primary({ constraintName: 'posts_pk' })
对于复合主键,使用下面展示的表级 table.primary([...])。
unique
在列上添加唯一索引。传入可选的 options 对象,包含 indexName 和 deferrable。
table.string('email').unique()
table.string('email').unique({ indexName: 'users_email_unique' })
对于复合唯一索引,使用表级 table.unique([...])。
index
在列上添加索引。传入可选的索引名和可选的索引类型(PostgreSQL 和 MySQL)。
table.string('slug').index()
table.string('slug').index('posts_slug_idx')
table.json('payload').index('posts_payload_gin', 'gin')
first 和 after
将列定位在表的开头(first)或特定列之后(after)。仅限 MySQL。
table.string('email').first()
table.string('avatar_url').after('password')
comment
在列上设置注释。
table.string('avatar_url').comment('Stored as a relative path')
collate
设置列的排序规则。仅限 MySQL。
table.string('email').collate('utf8_unicode_ci')
外键
外键可以作为列修饰符内联声明,也可以在表级别声明用于复合键。两种形式共享相同的下游方法。
references 和 inTable
定义引用的列和表。简写形式 references('table.column') 将两者合并。
// 完整形式
table.integer('user_id').references('id').inTable('users')
// 简写形式
table.integer('user_id').references('users.id')
// 表级别(允许复合键和命名约束)
table.foreign('user_id').references('users.id')
table.foreign(['tenant_id', 'user_id']).references(['tenant_id', 'id']).inTable('users')
onDelete 和 onUpdate
指定当引用行被删除或更新时采取的操作。标准 SQL 操作:CASCADE、SET NULL、RESTRICT、NO ACTION、SET DEFAULT。
table
.integer('user_id')
.references('users.id')
.onDelete('CASCADE')
.onUpdate('RESTRICT')
withKeyName
覆盖自动生成的外键约束名称。当你后续需要按名称删除或修改约束时很有用。
table
.integer('user_id')
.references('users.id')
.withKeyName('posts_user_id_fk')
deferrable
将外键标记为可延迟,使约束检查延迟到提交时。仅限 PostgreSQL。接受 'deferred'、'immediate' 或 'not deferrable'。
table
.integer('user_id')
.references('users.id')
.deferrable('deferred')
Check 约束
Check 约束对每个插入或更新的行强制执行谓词。在 PostgreSQL、MySQL 8+、MSSQL、SQLite 和 Oracle 上可用。每个方法接受可选的约束名作为最后一个参数。
checkPositive、checkNegative
table.integer('balance').checkPositive()
table.integer('temperature_below_zero').checkNegative('temp_must_be_negative')
checkIn 和 checkNotIn
table.string('status').checkIn(['draft', 'published', 'archived'])
table.string('locale').checkNotIn(['xx', 'yy'], 'locale_blacklist')
checkBetween
传入 [min, max] 元组表示单个范围,或元组数组表示多个可接受范围。
table.integer('rating').checkBetween([1, 5])
table.integer('hour').checkBetween([[0, 11], [13, 23]]) // 跳过 12
checkLength
table.string('handle').checkLength('>=', 3)
table.string('handle').checkLength('<=', 30, 'handle_max_length')
checkRegex
正则检查,写为 SQL 兼容的模式(方言特定语法)。
table.string('handle').checkRegex('^[a-z0-9_]+$')
dropChecks
删除在列上定义的所有 check 约束。
table.dropChecks()
表级约束
这些方法位于 table 构建器上,而非链式调用在列上。用于复合索引、复合外键和命名约束。
primary
定义单列或复合主键。
table.primary(['tenant_id', 'user_id'])
table.primary(['tenant_id', 'user_id'], { constraintName: 'tenant_user_pk' })
unique
定义单列或复合唯一索引。
table.unique(['slug', 'tenant_id'])
table.unique(['slug', 'tenant_id'], { indexName: 'posts_slug_tenant_unique' })
index
跨一个或多个列添加索引。
table.index(['first_name', 'last_name'])
table.index(['first_name', 'last_name'], 'users_full_name_idx')
table.index(['payload'], 'posts_payload_gin', 'gin') // PostgreSQL:索引类型
foreign
添加外键约束,包括复合键。可使用相同的 references、inTable、onDelete、onUpdate、withKeyName 和 deferrable 方法。
table.foreign('user_id').references('users.id').onDelete('CASCADE')
table
.foreign(['tenant_id', 'user_id'])
.references(['tenant_id', 'id'])
.inTable('users')
.withKeyName('posts_tenant_user_fk')
修改现有列
以下方法在 alterTable(或 this.schema.table(...))内部有效。
alter
将列定义标记为修改而非添加。修改是非增量的:你必须重新声明你希望列保留的每个约束。
this.schema.alterTable('posts', (table) => {
table.text('body').notNullable().alter()
})
传入选项以控制修改哪些方面:
table.text('body').alter({ alterNullable: true, alterType: false })
alter 在 SQLite 或 Redshift 上不受支持。
setNullable 和 dropNullable
切换现有列的可空性,而无需重新定义其类型。
this.schema.alterTable('users', (table) => {
table.setNullable('phone')
table.dropNullable('email')
})
当列已包含 NULL 值时,dropNullable 会失败;在应用之前先回填数据。
renameColumn
重命名列。
table.renameColumn('name', 'full_name')
删除列和约束
dropColumn 和 dropColumns
按名称删除一个或多个列。
table.dropColumn('legacy_field')
table.dropColumns('first_name', 'middle_name', 'last_name')
dropPrimary
删除主键约束。当约束使用自定义名称创建时,传入可选名称。
table.dropPrimary()
table.dropPrimary('posts_pk')
dropUnique
删除唯一索引。传入列和可选的索引名。
table.dropUnique(['email'])
table.dropUnique(['slug', 'tenant_id'], 'posts_slug_tenant_unique')
dropIndex
删除索引。传入列和可选的索引名。
table.dropIndex(['first_name', 'last_name'])
table.dropIndex(['first_name', 'last_name'], 'users_full_name_idx')
dropForeign
删除外键约束。传入列和可选的约束名。
table.dropForeign('user_id')
table.dropForeign(['tenant_id', 'user_id'], 'posts_tenant_user_fk')
dropTimestamps
删除由 timestamps() 添加的 created_at 和 updated_at 列。
table.dropTimestamps()
表选项
comment
在表上设置注释。
table.comment('Tracks every published article')
engine
设置存储引擎。仅限 MySQL。
table.engine('InnoDB')
charset
设置表级字符集。仅限 MySQL。
table.charset('utf8mb4')
collate
设置表级排序规则。仅限 MySQL。
table.collate('utf8mb4_unicode_ci')
inherits
设置用于继承的父表。仅限 PostgreSQL。
table.inherits('cities')