模型基础
在本教程中,您将学习 Sequelize 中的模型是什么以及如何使用它们。
概念
模型是 Sequelize 的核心。模型是表示数据库中表的抽象。在 Sequelize 中,它是一个扩展 Model 的类。
模型告诉 Sequelize 关于它所表示的实体的一些信息,例如数据库中表的名称以及它有哪些列(及其数据类型)。
Sequelize 中的模型有一个名称。此名称不必与它在数据库中表示的表的名称相同。通常,模型使用单数名称(例如 User
),而表使用复数名称(例如 Users
),尽管这是完全可配置的。
模型定义
模型可以在 Sequelize 中以两种等效的方式定义
定义模型后,可以通过其模型名称在 sequelize.models
中访问它。
为了通过示例学习,我们将考虑我们想要创建一个模型来表示用户,用户具有 firstName
和 lastName
。我们希望我们的模型称为 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!');
数据库安全检查
如上所示,sync
和 drop
操作具有破坏性。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 会自动为每个模型添加 createdAt
和 updatedAt
字段,使用数据类型 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 中,数据类型 INTEGER
、BIGINT
、FLOAT
和 DOUBLE
可以设置为无符号或零填充(或两者兼而有之),如下所示。
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.UUIDV1
或 DataTypes.UUIDV4
作为默认值即可。
{
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4 // Or DataTypes.UUIDV1
}
其他
还有其他数据类型,在 单独指南 中介绍。
列选项
在定义列时,除了指定列的 type
以及上面提到的 allowNull
和 defaultValue
选项外,还可以使用更多其他选项。下面是一些示例。
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'