跳至主要内容
版本:v6 - 稳定版

模型基础

在本教程中,您将学习 Sequelize 中的模型是什么以及如何使用它们。

概念

模型是 Sequelize 的核心。模型是表示数据库中表的抽象。在 Sequelize 中,它是一个扩展 Model 的类。

模型告诉 Sequelize 关于它所表示的实体的一些信息,例如数据库中表的名称以及它有哪些列(及其数据类型)。

Sequelize 中的模型有一个名称。此名称不必与它在数据库中表示的表的名称相同。通常,模型使用单数名称(例如 User),而表使用复数名称(例如 Users),尽管这是完全可配置的。

模型定义

模型可以在 Sequelize 中以两种等效的方式定义

定义模型后,可以通过其模型名称在 sequelize.models 中访问它。

为了通过示例学习,我们将考虑我们想要创建一个模型来表示用户,用户具有 firstNamelastName。我们希望我们的模型称为 User,它所表示的表在数据库中称为 Users

下面显示了定义此模型的两种方法。定义后,我们可以使用 sequelize.models.User 访问我们的模型。

使用 sequelize.define

const { Sequelize, DataTypes } = require('sequelize');
const sequelize = new Sequelize('sqlite::memory:');

const User = sequelize.define(
'User',
{
// Model attributes are defined here
firstName: {
type: DataTypes.STRING,
allowNull: false,
},
lastName: {
type: DataTypes.STRING,
// allowNull defaults to true
},
},
{
// Other model options go here
},
);

// `sequelize.define` also returns the model
console.log(User === sequelize.models.User); // true

扩展 Model

const { Sequelize, DataTypes, Model } = require('sequelize');
const sequelize = new Sequelize('sqlite::memory:');

class User extends Model {}

User.init(
{
// Model attributes are defined here
firstName: {
type: DataTypes.STRING,
allowNull: false,
},
lastName: {
type: DataTypes.STRING,
// allowNull defaults to true
},
},
{
// Other model options go here
sequelize, // We need to pass the connection instance
modelName: 'User', // We need to choose the model name
},
);

// the defined model is the class itself
console.log(User === sequelize.models.User); // true

在内部,sequelize.define 调用 Model.init,因此这两种方法本质上是等效的。

公共类字段的注意事项

添加与模型属性之一同名的 公共类字段 将会导致问题。Sequelize 为通过 Model.init 定义的每个属性添加了一个获取器和一个设置器。添加公共类字段将隐藏这些获取器和设置器,阻止访问模型的实际数据。

// Invalid
class User extends Model {
id; // this field will shadow sequelize's getter & setter. It should be removed.
otherPublicField; // this field does not shadow anything. It is fine.
}

User.init(
{
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
},
{ sequelize },
);

const user = new User({ id: 1 });
user.id; // undefined
// Valid
class User extends Model {
otherPublicField;
}

User.init(
{
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
},
{ sequelize },
);

const user = new User({ id: 1 });
user.id; // 1

在 TypeScript 中,您可以使用 declare 关键字添加类型信息,而无需添加实际的公共类字段

// Valid
class User extends Model {
declare id: number; // this is ok! The 'declare' keyword ensures this field will not be emitted by TypeScript.
}

User.init(
{
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
},
{ sequelize },
);

const user = new User({ id: 1 });
user.id; // 1

表名推断

请注意,在以上两种方法中,从未明确定义表名(Users)。但是,给出了模型名称(User)。

默认情况下,当未给出表名时,Sequelize 会自动将模型名称变为复数,并将其用作表名。此复数化由名为 inflection 的库在后台完成,以便正确计算不规则复数(例如 person -> people)。

当然,此行为很容易配置。

强制表名等于模型名

您可以使用 freezeTableName: true 选项停止 Sequelize 执行的自动复数化。这样,Sequelize 将推断表名等于模型名,没有任何修改

sequelize.define(
'User',
{
// ... (attributes)
},
{
freezeTableName: true,
},
);

上面的示例将创建一个名为 User 的模型,该模型指向一个也名为 User 的表。

此行为也可以在创建 Sequelize 实例时全局定义

const sequelize = new Sequelize('sqlite::memory:', {
define: {
freezeTableName: true,
},
});

这样,所有表都将使用与模型名称相同的名称。

直接提供表名

您也可以简单地直接告诉 Sequelize 表的名称

sequelize.define(
'User',
{
// ... (attributes)
},
{
tableName: 'Employees',
},
);

模型同步

当您定义模型时,您是在告诉 Sequelize 关于其在数据库中的表的一些信息。但是,如果表实际上根本不存在于数据库中怎么办?如果它存在,但它有不同的列、较少的列或任何其他差异怎么办?

这就是模型同步发挥作用的地方。可以通过调用 model.sync(options)(一个返回 Promise 的异步函数)来将模型与数据库同步。通过此调用,Sequelize 将自动对数据库执行 SQL 查询。请注意,这只会更改数据库中的表,而不是 JavaScript 端的模型。

  • User.sync() - 如果表不存在,则创建它(如果它已经存在,则不执行任何操作)
  • User.sync({ force: true }) - 创建表,如果它已经存在则先删除它
  • User.sync({ alter: true }) - 检查数据库中表的当前状态(它有哪些列,它们的数据类型是什么等),然后对表执行必要的更改以使其与模型匹配。

示例

await User.sync({ force: true });
console.log('The table for the User model was just (re)created!');

一次同步所有模型

您可以使用 sequelize.sync() 自动同步所有模型。示例

await sequelize.sync({ force: true });
console.log('All models were synchronized successfully.');

删除表

要删除与模型相关的表

await User.drop();
console.log('User table dropped!');

要删除所有表

await sequelize.drop();
console.log('All tables dropped!');

数据库安全检查

如上所示,syncdrop 操作具有破坏性。Sequelize 接受一个 match 选项作为额外的安全检查,该选项接收一个 RegExp

// This will run .sync() only if database name ends with '_test'
sequelize.sync({ force: true, match: /_test$/ });

生产环境中的同步

如上所示,sync({ force: true })sync({ alter: true }) 可能是破坏性操作。因此,不建议在生产级软件中使用它们。相反,同步应该使用更高级的概念 迁移 来完成,并借助 Sequelize CLI

时间戳

默认情况下,Sequelize 会自动为每个模型添加 createdAtupdatedAt 字段,使用数据类型 DataTypes.DATE。这些字段也会自动管理 - 每当你使用 Sequelize 创建或更新数据时,这些字段都会被正确设置。createdAt 字段将包含表示创建时刻的时间戳,而 updatedAt 将包含最新更新的时间戳。

注意:这是在 Sequelize 层级完成的(即,不是通过SQL 触发器完成的)。这意味着直接的 SQL 查询(例如,通过任何其他方式执行的、未经 Sequelize 的查询)不会导致这些字段自动更新。

可以使用 timestamps: false 选项为模型禁用此行为。

sequelize.define(
'User',
{
// ... (attributes)
},
{
timestamps: false,
},
);

也可以只启用 createdAt/updatedAt 中的一个,并为这些列提供自定义名称。

class Foo extends Model {}
Foo.init(
{
/* attributes */
},
{
sequelize,

// don't forget to enable timestamps!
timestamps: true,

// I don't want createdAt
createdAt: false,

// I want updatedAt to actually be called updateTimestamp
updatedAt: 'updateTimestamp',
},
);

列声明简写语法

如果仅为列指定其数据类型,则可以简化语法。

// This:
sequelize.define('User', {
name: {
type: DataTypes.STRING,
},
});

// Can be simplified to:
sequelize.define('User', { name: DataTypes.STRING });

默认值

默认情况下,Sequelize 假设列的默认值为 NULL。可以通过向列定义传递特定的 defaultValue 来更改此行为。

sequelize.define('User', {
name: {
type: DataTypes.STRING,
defaultValue: 'John Doe',
},
});

也接受一些特殊值,例如 DataTypes.NOW

sequelize.define('Foo', {
bar: {
type: DataTypes.DATETIME,
defaultValue: DataTypes.NOW,
// This way, the current date/time will be used to populate this column (at the moment of insertion)
},
});

数据类型

你在模型中定义的每一列都必须具有数据类型。Sequelize 提供了 许多内置数据类型。要访问内置数据类型,必须导入 DataTypes

const { DataTypes } = require('sequelize'); // Import the built-in data types

字符串

DataTypes.STRING; // VARCHAR(255)
DataTypes.STRING(1234); // VARCHAR(1234)
DataTypes.STRING.BINARY; // VARCHAR BINARY
DataTypes.TEXT; // TEXT
DataTypes.TEXT('tiny'); // TINYTEXT
DataTypes.CITEXT; // CITEXT PostgreSQL and SQLite only.
DataTypes.TSVECTOR; // TSVECTOR PostgreSQL only.

布尔值

DataTypes.BOOLEAN; // TINYINT(1)

数字

DataTypes.INTEGER; // INTEGER
DataTypes.BIGINT; // BIGINT
DataTypes.BIGINT(11); // BIGINT(11)

DataTypes.FLOAT; // FLOAT
DataTypes.FLOAT(11); // FLOAT(11)
DataTypes.FLOAT(11, 10); // FLOAT(11,10)

DataTypes.REAL; // REAL PostgreSQL only.
DataTypes.REAL(11); // REAL(11) PostgreSQL only.
DataTypes.REAL(11, 12); // REAL(11,12) PostgreSQL only.

DataTypes.DOUBLE; // DOUBLE
DataTypes.DOUBLE(11); // DOUBLE(11)
DataTypes.DOUBLE(11, 10); // DOUBLE(11,10)

DataTypes.DECIMAL; // DECIMAL
DataTypes.DECIMAL(10, 2); // DECIMAL(10,2)

无符号和零填充整数 - 仅限 MySQL/MariaDB

在 MySQL 和 MariaDB 中,数据类型 INTEGERBIGINTFLOATDOUBLE 可以设置为无符号或零填充(或两者兼而有之),如下所示。

DataTypes.INTEGER.UNSIGNED;
DataTypes.INTEGER.ZEROFILL;
DataTypes.INTEGER.UNSIGNED.ZEROFILL;
// You can also specify the size i.e. INTEGER(10) instead of simply INTEGER
// Same for BIGINT, FLOAT and DOUBLE

日期

DataTypes.DATE; // DATETIME for mysql / sqlite, TIMESTAMP WITH TIME ZONE for postgres
DataTypes.DATE(6); // DATETIME(6) for mysql 5.6.4+. Fractional seconds support with up to 6 digits of precision
DataTypes.DATEONLY; // DATE without time

UUID

对于 UUID,使用 DataTypes.UUID。它将成为 PostgreSQL 和 SQLite 的 UUID 数据类型,以及 MySQL 的 CHAR(36)。Sequelize 可以为这些字段自动生成 UUID,只需使用 DataTypes.UUIDV1DataTypes.UUIDV4 作为默认值即可。

{
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4 // Or DataTypes.UUIDV1
}

其他

还有其他数据类型,在 单独指南 中介绍。

列选项

在定义列时,除了指定列的 type 以及上面提到的 allowNulldefaultValue 选项外,还可以使用更多其他选项。下面是一些示例。

const { Model, DataTypes, Deferrable } = require('sequelize');

class Foo extends Model {}
Foo.init(
{
// instantiating will automatically set the flag to true if not set
flag: { type: DataTypes.BOOLEAN, allowNull: false, defaultValue: true },

// default values for dates => current time
myDate: { type: DataTypes.DATE, defaultValue: DataTypes.NOW },

// setting allowNull to false will add NOT NULL to the column, which means an error will be
// thrown from the DB when the query is executed if the column is null. If you want to check that a value
// is not null before querying the DB, look at the validations section below.
title: { type: DataTypes.STRING, allowNull: false },

// Creating two objects with the same value will throw an error. The unique property can be either a
// boolean, or a string. If you provide the same string for multiple columns, they will form a
// composite unique key.
uniqueOne: { type: DataTypes.STRING, unique: 'compositeIndex' },
uniqueTwo: { type: DataTypes.INTEGER, unique: 'compositeIndex' },

// The unique property is simply a shorthand to create a unique constraint.
someUnique: { type: DataTypes.STRING, unique: true },

// Go on reading for further information about primary keys
identifier: { type: DataTypes.STRING, primaryKey: true },

// autoIncrement can be used to create auto_incrementing integer columns
incrementMe: { type: DataTypes.INTEGER, autoIncrement: true },

// You can specify a custom column name via the 'field' attribute:
fieldWithUnderscores: {
type: DataTypes.STRING,
field: 'field_with_underscores',
},

// It is possible to create foreign keys:
bar_id: {
type: DataTypes.INTEGER,

references: {
// This is a reference to another model
model: Bar,

// This is the column name of the referenced model
key: 'id',

// With PostgreSQL, it is optionally possible to declare when to check the foreign key constraint, passing the Deferrable type.
deferrable: Deferrable.INITIALLY_IMMEDIATE,
// Options:
// - `Deferrable.INITIALLY_IMMEDIATE` - Immediately check the foreign key constraints
// - `Deferrable.INITIALLY_DEFERRED` - Defer all foreign key constraint check to the end of a transaction
// - `Deferrable.NOT` - Don't defer the checks at all (default) - This won't allow you to dynamically change the rule in a transaction
},
},

// Comments can only be added to columns in MySQL, MariaDB, PostgreSQL and MSSQL
commentMe: {
type: DataTypes.INTEGER,
comment: 'This is a column name that has a comment',
},
},
{
sequelize,
modelName: 'foo',

// Using `unique: true` in an attribute above is exactly the same as creating the index in the model's options:
indexes: [{ unique: true, fields: ['someUnique'] }],
},
);

利用模型是类

Sequelize 模型是 ES6 类。你可以非常轻松地添加自定义实例或类级别方法。

class User extends Model {
static classLevelMethod() {
return 'foo';
}
instanceLevelMethod() {
return 'bar';
}
getFullname() {
return [this.firstname, this.lastname].join(' ');
}
}
User.init(
{
firstname: Sequelize.TEXT,
lastname: Sequelize.TEXT,
},
{ sequelize },
);

console.log(User.classLevelMethod()); // 'foo'
const user = User.build({ firstname: 'Jane', lastname: 'Doe' });
console.log(user.instanceLevelMethod()); // 'bar'
console.log(user.getFullname()); // 'Jane Doe'