表模型
表模型
在使用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
自动向每个模型添加 createdAt
和 updatedAt
字段。
使用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});
列参数
在定义列时,除了指定列的 type
、allowNull
和 defaultValue
参数外,还有很多可用的参数,下面分类介绍
默认值
默认情况下,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
字段配置,给数据库表字段增加注释说明。目前只能支持添加到 MySQL
、MariaDB
、PostgreSQL
和 MSSQL
的列中。例如:
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
}
}
}