Model Basics - 模型基础

在本教程中,你将学习 Sequelize 中的模型以及如何使用它们.

概念

模型是 Sequelize 的本质. 模型是代表数据库中表的抽象. 在 Sequelize 中,它是一个 Model 的扩展类.

该模型告诉 Sequelize 有关它代表的实体的几件事,例如数据库中表的名称以及它具有的列(及其数据类型).

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

模型定义

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

定义模型后,可通过其模型名称在 sequelize.models 中使用该模型.

为了学习一个示例,我们将考虑创建一个代表用户的模型,该模型具有一个 firstName 和一个 lastName. 我们希望将模型称为 User,并将其表示的表在数据库中称为 Users.

定义该模型的两种方法如下所示. 定义后,我们可以使用 sequelize.models.User 访问模型.

使用 sequelize.define:

  1. const { Sequelize, DataTypes } = require('sequelize');
  2. const sequelize = new Sequelize('sqlite::memory:');
  3. const User = sequelize.define('User', {
  4. // 在这里定义模型属性
  5. firstName: {
  6. type: DataTypes.STRING,
  7. allowNull: false
  8. },
  9. lastName: {
  10. type: DataTypes.STRING
  11. // allowNull 默认为 true
  12. }
  13. }, {
  14. // 这是其他模型参数
  15. });
  16. // `sequelize.define` 会返回模型
  17. console.log(User === sequelize.models.User); // true

扩展 Model

  1. const { Sequelize, DataTypes, Model } = require('sequelize');
  2. const sequelize = new Sequelize('sqlite::memory');
  3. class User extends Model {}
  4. User.init({
  5. // 在这里定义模型属性
  6. firstName: {
  7. type: DataTypes.STRING,
  8. allowNull: false
  9. },
  10. lastName: {
  11. type: DataTypes.STRING
  12. // allowNull 默认为 true
  13. }
  14. }, {
  15. // 这是其他模型参数
  16. sequelize, // 我们需要传递连接实例
  17. modelName: 'User' // 我们需要选择模型名称
  18. });
  19. // 定义的模型是类本身
  20. console.log(User === sequelize.models.User); // true

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

表名推断

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

默认情况下,当未提供表名时,Sequelize 会自动将模型名复数并将其用作表名. 这种复数是通过称为 inflection 的库在后台完成的,因此可以正确计算不规则的复数(例如 person -> people).

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

强制表名称等于模型名称

你可以使用 freezeTableName: true 参数停止 Sequelize 执行自动复数化. 这样,Sequelize 将推断表名称等于模型名称,而无需进行任何修改:

  1. sequelize.define('User', {
  2. // ... (属性)
  3. }, {
  4. freezeTableName: true
  5. });

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

也可以为 sequelize 实例全局定义此行为:

  1. const sequelize = new Sequelize('sqlite::memory:', {
  2. define: {
  3. freezeTableName: true
  4. }
  5. });

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

直接提供表名

你也可以直接直接告诉 Sequelize 表名称:

  1. sequelize.define('User', {
  2. // ... (属性)
  3. }, {
  4. tableName: 'Employees'
  5. });

模型同步

定义模型时,你要告诉 Sequelize 有关数据库中表的一些信息. 但是,如果该表实际上不存在于数据库中怎么办? 如果存在,但具有不同的列,较少的列或任何其他差异,该怎么办?

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

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

示例:

  1. await User.sync({ force: true });
  2. console.log("用户模型表刚刚(重新)创建!");

一次同步所有模型

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

  1. await sequelize.sync({ force: true });
  2. console.log("所有模型均已成功同步.");

删除表

删除与模型相关的表:

  1. await User.drop();
  2. console.log("用户表已删除!");

删除所有表:

  1. await sequelize.drop();
  2. console.log("所有表已删除!");

数据库安全检查

如上所示,syncdrop操作是破坏性的. Sequelize 使用 match 参数作为附加的安全检查,该检查将接受 RegExp:

  1. // 仅当数据库名称以 '_test' 结尾时,它才会运行.sync()
  2. sequelize.sync({ force: true, match: /_test$/ });

生产环境同步

如上所示,sync({ force: true })sync({ alter: true }) 可能是破坏性操作. 因此,不建议将它们用于生产级软件中. 相反,应该在 Sequelize CLI 的帮助下使用高级概念 Migrations(迁移) 进行同步.

时间戳

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

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

对于带有 timestamps: false 参数的模型,可以禁用此行为:

  1. sequelize.define('User', {
  2. // ... (属性)
  3. }, {
  4. timestamps: false
  5. });

也可以只启用 createdAt/updatedAt 之一,并为这些列提供自定义名称:

  1. class Foo extends Model {}
  2. Foo.init({ /* 属性 */ }, {
  3. sequelize,
  4. // 不要忘记启用时间戳!
  5. timestamps: true,
  6. // 不想要 createdAt
  7. createdAt: false,
  8. // 想要 updatedAt 但是希望名称叫做 updateTimestamp
  9. updatedAt: 'updateTimestamp'
  10. });

列声明简写语法

如果关于列的唯一指定内容是其数据类型,则可以缩短语法:

  1. // 例如:
  2. sequelize.define('User', {
  3. name: {
  4. type: DataTypes.STRING
  5. }
  6. });
  7. // 可以简写为:
  8. sequelize.define('User', { name: DataTypes.STRING });

默认值

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

  1. sequelize.define('User', {
  2. name: {
  3. type: DataTypes.STRING,
  4. defaultValue: "John Doe"
  5. }
  6. });

一些特殊的值,例如 Sequelize.NOW,也能被接受:

  1. sequelize.define('Foo', {
  2. bar: {
  3. type: DataTypes.DATETIME,
  4. defaultValue: Sequelize.NOW
  5. // 这样,当前日期/时间将用于填充此列(在插入时)
  6. }
  7. });

数据类型

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

  1. const { DataTypes } = require("sequelize"); // 导入内置数据类型

字符串

  1. DataTypes.STRING // VARCHAR(255)
  2. DataTypes.STRING(1234) // VARCHAR(1234)
  3. DataTypes.STRING.BINARY // VARCHAR BINARY
  4. DataTypes.TEXT // TEXT
  5. DataTypes.TEXT('tiny') // TINYTEXT
  6. DataTypes.CITEXT // CITEXT 仅 PostgreSQL 和 SQLite.

布尔

  1. DataTypes.BOOLEAN // TINYINT(1)

数字

  1. DataTypes.INTEGER // INTEGER
  2. DataTypes.BIGINT // BIGINT
  3. DataTypes.BIGINT(11) // BIGINT(11)
  4. DataTypes.FLOAT // FLOAT
  5. DataTypes.FLOAT(11) // FLOAT(11)
  6. DataTypes.FLOAT(11, 10) // FLOAT(11,10)
  7. DataTypes.REAL // REAL 仅 PostgreSQL.
  8. DataTypes.REAL(11) // REAL(11) 仅 PostgreSQL.
  9. DataTypes.REAL(11, 12) // REAL(11,12) 仅 PostgreSQL.
  10. DataTypes.DOUBLE // DOUBLE
  11. DataTypes.DOUBLE(11) // DOUBLE(11)
  12. DataTypes.DOUBLE(11, 10) // DOUBLE(11,10)
  13. DataTypes.DECIMAL // DECIMAL
  14. DataTypes.DECIMAL(10, 2) // DECIMAL(10,2)

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

在 MySQL 和 MariaDB 中,可以将数据类型INTEGER, BIGINT, FLOATDOUBLE 设置为无符号或零填充(或两者),如下所示:

  1. DataTypes.INTEGER.UNSIGNED
  2. DataTypes.INTEGER.ZEROFILL
  3. DataTypes.INTEGER.UNSIGNED.ZEROFILL
  4. // 你还可以指定大小,即INTEGER(10)而不是简单的INTEGER
  5. // 同样适用于 BIGINT, FLOAT 和 DOUBLE

日期

  1. DataTypes.DATE // DATETIME 适用于 mysql / sqlite, 带时区的TIMESTAMP 适用于 postgres
  2. DataTypes.DATE(6) // DATETIME(6) 适用于 mysql 5.6.4+. 支持6位精度的小数秒
  3. DataTypes.DATEONLY // 不带时间的 DATE

UUID

对于 UUID,使用 DataTypes.UUID. 对于 PostgreSQL 和 SQLite,它会是 UUID 数据类型;对于 MySQL,它则变成CHAR(36). Sequelize 可以自动为这些字段生成 UUID,只需使用 Sequelize.UUIDV1Sequelize.UUIDV4 作为默认值即可:

  1. {
  2. type: DataTypes.UUID,
  3. defaultValue: Sequelize.UUIDV4 // 或 Sequelize.UUIDV1
  4. }

其它

还有其他数据类型,请参见其它数据类型.

列参数

在定义列时,除了指定列的 type 以及上面提到的 allowNulldefaultValue 参数外,还有很多可用的参数. 下面是一些示例.

  1. const { Model, DataTypes, Deferrable } = require("sequelize");
  2. class Foo extends Model {}
  3. Foo.init({
  4. // 实例化将自动将 flag 设置为 true (如果未设置)
  5. flag: { type: DataTypes.BOOLEAN, allowNull: false, defaultValue: true },
  6. // 日期的默认值 => 当前时间
  7. myDate: { type: DataTypes.DATE, defaultValue: DataTypes.NOW },
  8. // 将 allowNull 设置为 false 将为该列添加 NOT NULL,
  9. // 这意味着如果该列为 null,则在执行查询时将从数据库引发错误.
  10. // 如果要在查询数据库之前检查值是否不为 null,请查看下面的验证部分.
  11. title: { type: DataTypes.STRING, allowNull: false },
  12. // 创建两个具有相同值的对象将引发错误.
  13. // unique 属性可以是布尔值或字符串.
  14. // 如果为多个列提供相同的字符串,则它们将形成一个复合唯一键.
  15. uniqueOne: { type: DataTypes.STRING, unique: 'compositeIndex' },
  16. uniqueTwo: { type: DataTypes.INTEGER, unique: 'compositeIndex' },
  17. // unique 属性是创建唯一约束的简写.
  18. someUnique: { type: DataTypes.STRING, unique: true },
  19. // 继续阅读有关主键的更多信息
  20. identifier: { type: DataTypes.STRING, primaryKey: true },
  21. // autoIncrement 可用于创建 auto_incrementing 整数列
  22. incrementMe: { type: DataTypes.INTEGER, autoIncrement: true },
  23. // 你可以通过 'field' 属性指定自定义列名称:
  24. fieldWithUnderscores: { type: DataTypes.STRING, field: 'field_with_underscores' },
  25. // 可以创建外键:
  26. bar_id: {
  27. type: DataTypes.INTEGER,
  28. references: {
  29. // 这是对另一个模型的参考
  30. model: Bar,
  31. // 这是引用模型的列名
  32. key: 'id',
  33. // 使用 PostgreSQL,可以通过 Deferrable 类型声明何时检查外键约束.
  34. deferrable: Deferrable.INITIALLY_IMMEDIATE
  35. // 参数:
  36. // - `Deferrable.INITIALLY_IMMEDIATE` - 立即检查外键约束
  37. // - `Deferrable.INITIALLY_DEFERRED` - 将所有外键约束检查推迟到事务结束
  38. // - `Deferrable.NOT` - 完全不推迟检查(默认) - 这将不允许你动态更改事务中的规则
  39. }
  40. },
  41. // 注释只能添加到 MySQL,MariaDB,PostgreSQL 和 MSSQL 的列中
  42. commentMe: {
  43. type: DataTypes.INTEGER,
  44. comment: '这是带有注释的列'
  45. }
  46. }, {
  47. sequelize,
  48. modelName: 'foo',
  49. // 在上面的属性中使用 `unique: true` 与在模型的参数中创建索引完全相同:
  50. indexes: [{ unique: true, fields: ['someUnique'] }]
  51. });

利用模型作为类

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

  1. class User extends Model {
  2. static classLevelMethod() {
  3. return 'foo';
  4. }
  5. instanceLevelMethod() {
  6. return 'bar';
  7. }
  8. getFullname() {
  9. return [this.firstname, this.lastname].join(' ');
  10. }
  11. }
  12. User.init({
  13. firstname: Sequelize.TEXT,
  14. lastname: Sequelize.TEXT
  15. }, { sequelize });
  16. console.log(User.classLevelMethod()); // 'foo'
  17. const user = User.build({ firstname: 'Jane', lastname: 'Doe' });
  18. console.log(user.instanceLevelMethod()); // 'bar'
  19. console.log(user.getFullname()); // 'Jane Doe'