跳至主要內容

表模型

微信公众号:储凡About 22 min

表模型

在使用ORM框架去操作数据库的时候,模型是 Sequelize 的本质。模型是代表数据库中表的抽象。 在 Sequelize框架中,模型是一个 Model 的扩展类。

模型往往和表字段是一一对应的,这样非常利于封装,类似于 Java 语言中的MyBatis

模型定义

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

  • 调用 sequelize.define(modelName, attributes, options)
  • 扩展 Model 并调用 init(attributes, options)

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

模型的定义最重要是与表中的列要做到关系映射,例如:

定义一个基础的用户表的模型

const { DataTypes } = require('sequelize')

export const UserModel = {
  /**
   * 名称
   */
  name: DataTypes.STRING,

  /**
   * 年龄
   */
  age: DataTypes.INTEGER,
  /**
   * 性别
   */
  gender: {
    type: DataTypes.BOOLEAN,
    // 默认值
    defaultValue: 1
  },
  /**
   * 出生日期
   */
  birth: {
    type: DataTypes.DATE
  }
}

使用define()函数

const { Sequelize, DataTypes } = require('sequelize')
const { UserModel } = require('./user')
const sequelize = new Sequelize('sqlite::memory:')

const User = sequelize.define('User', UserModel)

// `sequelize.define` 会返回模型
console.log(User === sequelize.models.User) // true

模型定义后,可以在使用挂载在sequelize对象上的模型实例sequelize.models.User

使用init()函数

const { Sequelize, DataTypes, Model } = require('sequelize')
const { UserModel } = require('./user')
const sequelize = new Sequelize('sqlite::memory')


/**
 * 继承Model类型,使用使用init方法注册模型
 */
class User extends Model {

}

/**
 * 初始化模型
 */
User.init(UserModel, {
  // 连接实例
  sequelize,
  // 指定模型绑在sequelize对象上的名称
  modelName: 'User'
})

// 定义的模型就是User类本身
console.log(User === sequelize.models.User) // true

定义后,我们可以使用 sequelize.models.User 访问模型。

表名规则

上面两种方法的模型定义中,都未明确定义表名(users)。 但是,给出了模型名称(User)。

默认情况下,当未提供表名时,Sequelize框架会自动将模型名复数用作表名。

强制表名等于模型名

可以使用 freezeTableName: true 配置, Sequelize框架将自动执行模型名的复数形式, 推断出表名称等于模型名称,不需要进行任何修改。例如:

const {Sequelize} = require('sequelize')
const sequelize = new Sequelize('postgres://user:pass@142vip.cn:5432/142vip')

sequelize.define('User', {
  // ... (属性)
}, {
  // 强制表名等于模型名
  freezeTableName: true
});

上面代码将创建一个名为 User 的模型,该模型指向一个也名为 users 的表。Sequelize框架也支持全局配置这个参数,只需要在创建sequelize连接实例时指定,例如:

const {Sequelize} = require('sequelize')

const sequelize = new Sequelize('postgres://user:pass@142vip.cn:5432/142vip', {
  define: {
    // 强制所有表名称等于模型名称
    freezeTableName: true
  }
});

直接提供表名

对于模型与表之间的映射关系,本质是需要告诉Sequelize框架对应的数据库名称。 除了强制对模型名称进行转换, 还可以在模型定义式直接指定表名,使用tableName参数进行配置。例如:

建议数据库表名以tbl开头,例如: tbl_user

const {Sequelize} = require('sequelize')
const sequelize = new Sequelize('postgres://user:pass@142vip.cn:5432/142vip')

sequelize.define('User', {
  // ... (属性)
}, {
  // 指定对应的表名
  tableName: 'tbl_user'
});

此时,在freezeTableName参数默认为false,不会将模型名与表名进行转换,只会根据tableName字段来制定表名

同步模型

定义模型时,要告诉 Sequelize 有关数据库中表的一些信息。 但是:

  • 如果该表实际上不存在于数据库中怎么办?
  • 如果存在,但具有不同的列、缺少列或存在任何其他差异,该如何处理?

这两种情况,都是预期情况与实际情况产生差异时,会经常存在的,这也是同步模型这功能极需要的问题。

可以通过调用一个异步函数model.sync(options),返回的是一个Promise对象 。 Sequelize框架将自动对数据库执行 SQL 查询。

特别注意:这仅更改数据库中的表,而不更改 JavaScript 端的模型.+

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

// 不会删除表,只会同步与模型中存在,但表中不存在的字段
await User.sync({force: false, alter: true})

同步所有模型

可以在创建Sequelize连接实例后使用 sequelize.sync() 自动同步所有模型。 示例:

// 删除所有表,将连接实例上挂载的模型全部同步到表中
await sequelize.sync({force: true});

sync({ force: true })sync({ alter: true }) 可能是破坏性操作, 一旦输出数据库,只能通过数据库日志进行恢复。不建议将它们用于生产级环境中。

删除表

Sequelize框架提供了删除模型对应的数据库表的API,可以使用drop()方法实现。

// 删除单个表,例如:User
await User.drop();

// 删除所有表
await sequelize.drop();

注意:

  • 删除单个表时,是对应表的模型对象调用drop()方法实现的。
  • 删除所有表时,是当前连接的sequelize实例调用drop()方法实现的。

时间戳的使用

默认情况下,Sequelize 框架使用数据类型 DataTypes.DATE 自动向每个模型添加 createdAtupdatedAt 字段。

使用Sequelize 创建或更新内容时,字段都会被自动设置

  • createdAt 字段将包含代表创建时刻的时间戳
  • updatedAt 字段将包含最新更新的时间戳.

特别需要注意的是:上面的字段自动管理是在 Sequelize 级别使用SQL触发器(钩子函数)完成的

直接通过原始的SQL 查询,不会触发SQL钩子函数,也就导致无法实现该字段的自动更新

const {Sequelize} = require('sequelize')

const sequelize = new Sequelize('postgres://user:pass@142vip.cn:5432/142vip');
// 配置单个表的字段自动管理
sequelize.define('User', {
  // ... (属性)
}, {
  // 是否禁用字段自动管理
  timestamps: false
});

也可以只启用 createdAt/updatedAt 之一,支持自定义列名称,例如:

class User extends Model {
}

User.init({
  // ... (属性)
}, {
  sequelize,
  // 这里时间戳必须启用true
  timestamps: true,
  // 不使用createdAt字段
  createdAt: false,
  // 使用 updatedAt字段 但是希望名称叫做 create_time
  updatedAt: 'create_time'
});

同样的,也支持全局配置,例如:

const {Sequelize} = require('sequelize')

const sequelize = new Sequelize('postgres://user:pass@142vip.cn:5432/142vip', {
  define: {
    timestamps: true,
    // 不使用createdAt字段
    createdAt: false,
    // 使用 updatedAt字段 但是希望名称叫做 create_time
    updatedAt: 'create_time'
  }
});

列声明简写

在模型定义中,如果只指定列的数据类型,可以缩短语法,例如:

const {Sequelize} = require('sequelize')
const sequelize = new Sequelize('postgres://user:pass@142vip.cn:5432/142vip');

// 指定字段name的数据类型
sequelize.define('User', {
  name: {
    type: DataTypes.STRING
  }
});

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

列参数

在定义列时,除了指定列的 typeallowNulldefaultValue 参数外,还有很多可用的参数,下面分类介绍

默认值

默认情况下,Sequelize 假定列的默认值为 NULL。 可以通过将特定的 defaultValue字段来设置模型列的默认值,例如:

const {Sequelize} = require('sequelize')
const sequelize = new Sequelize('postgres://user:pass@142vip.cn:5432/142vip');

sequelize.define('User', {
  name: {
    type: DataTypes.STRING,
    defaultValue: "142vip.cn"
  }
});

一些特殊的值,例如 DataTypes.NOW,也可以正常使用:

const {Sequelize} = require('sequelize')
const sequelize = new Sequelize('postgres://user:pass@142vip.cn:5432/142vip');

sequelize.define('User', {
  name: {
    type: DataTypes.STRING,
    defaultValue: "142vip.cn"
  },
  createTime: {
    // datatime类型
    type: DataTypes.DATETIME,
    // 当前日期/时间
    defaultValue: DataTypes.NOW
  }
});

允许空

使用 allowNull 字段配置,设置模型列是否为空。 将 allowNull 设置为 false 将为该列添加 NOT NULL, 这意味着如果该列为null,则在执行查询时将从数据库引发错误。例如:

const {DataTypes} = require("sequelize");

class User {
  title: {
    type: DataTypes.STRING,
    allowNull: false
  }
}

唯一索引

使用 unique 字段配置,创建唯一索引。例如:

const {DataTypes} = require("sequelize");

class User {
  // unique 属性是创建唯一约束的简写
  name: {
    type: DataTypes.STRING,
    unique: true
  }
}

主键

使用 primaryKey 字段配置,创建主键,字段值唯一。例如:

const {DataTypes} = require("sequelize");

class User {
  // 创建索引
  id: {
    type: DataTypes.INTEGER,
    primaryKey: true
  }
}

自增

使用 autoIncrement 字段配置,设置列属性自增,默认从1开始。例如:

class User {
  // 创建自增字段
  id: {
    type: DataTypes.INTEGER,
    autoIncrement: true
  }
}

指定列名

使用 field 字段配置,设置模型对应的数据库表列名。例如:

class User {
  name: {
    type: DataTypes.STRING,
    field: 'name',
    comment: '用户名'
  }
}

列注释

使用 comment 字段配置,给数据库表字段增加注释说明。目前只能支持添加到 MySQLMariaDBPostgreSQLMSSQL 的列中。例如:

class User {
  // 添加注释
  name: {
    type: DataTypes.INTEGER,
    comment: '这是带有注释的列'
  }
}

创建外键

使用 references 字段配置,支持模型创建外键关联。references参数支持的配置有:

  • model: - 指定模型,表明与那个模型建立外键关联
  • key: 指定列名,表明与那个列建立关联信息
  • deferrable:使用 PostgreSQL,可以通过 Deferrable 类型声明何时检查外键约束,可以配置:
    • Deferrable.INITIALLY_IMMEDIATE: 立即检查外键约束
    • Deferrable.INITIALLY_DEFERRED: 将所有外键约束检查推迟到事务结束
    • Deferrable.NOT: 完全不推迟检查(默认) 将不允许动态更改事务中的规则

这里将 User 表中的 id 字段与 Account 表中的 id 字段建立外键关联关系,例如:

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

/**
 * 建立外键关联关系
 */
class User {
  id: {
    type: DataTypes.INTEGER,
    references: {
      // 模型名,
      model: Account,
      // 引用模型的列名
      key: 'id',
      // 声明什么时候检查外键约束.
      deferrable: Deferrable.INITIALLY_IMMEDIATE
    }
  }
}