Sequelize 使用总结

1,745 阅读6分钟

- 基础使用

  • 初始化 new Sequlize({}) 配置
    • { define } 及是 Model.init 配置。define 只能定义 Model.init 里 options 的相关内容
    • 默认会给模型添加id/createdAt/updatedAt等字段,可以更改
  • 其他方法
    • 原始查询const [results, metadata] = sequelize.query(mysql)
    • sequelize.fn(fn, args) 用于创建数据库函数对象。在where/order中使用
    • sequelize.col(string) 创建数据库列对象。建议使用 table.colum 完整命名方式
      • attributes: {[Sequelize.fn('DISTINCT', Sequelize.col('external_info.status_code')), 'statusCode']}
      • [Sequelize.fn('count', Sequelize.col('id')), 'num']
  • :可配合 npm i -g sequelize-auto 自动获取已有表生成对应的 model
// 基本配置
new Sequelize({
  //...
  define: {
    charset: 'utf8mb4',
    collate: 'utf8mb4_general_ci',
    createdAt: 'createTime',
    updatedAt: 'updateTime',
    deleteAt: 'deleteTime',
    underscored: true, // _命名规则
    freezeTableName: true // 强制表名称等于模型名称
  }
});

// 数据列配置项
{ type, allowNull: true, defautlValue, unique, primaryKey, autoIncrement, comment, field, validate }

// 定义表 - 1:sequlize.define 内部实际调用的 Model.init
sequlize.define('modelName', { column_field }, { paranoid });

// 定义表 - 2
const { Model } = require('sequlize');
const sequelize = new Sequelize('sqlite::memory');

class User extends Model {
  // 可以创建一些自定义 模块数据 函数
  getFullName() {
  	return [this.firstname, this.lastname].join(' ');
  }
}

User.init({ column_field }, {
  // 这是其他模型参数
  sequelize,
  tableName: 'xxx_xxx'
})

- 数据类型

  • TEXT(length)
  • STRING(length)
  • CHAR(length)
  • NUMBER(options)
    • optons
      • .length/.zerofill/.unsigned/.decimals/.precision/.scale
    • TINYINT(options)
    • INTEGER(options)
    • BIGINT(options)
    • FLOAT(length, decimals)
    • DOUBLE(length, decimals)
    • DECIMAL(precision, scale)
  • BOOLEAN 便捷 TINYINT(1)
  • DATE(length): Date column with timezone, default is UTC
  • DATEONLY: A date only column (no timestamp)
  • NOW: A default value of the current timestamp
  • BLOB(length)

-虚拟字段、get/set

  • .define(ModelName, { columns }) 时可设置 **get **并调用 this.getDataValue( columnName )
    • 本字段 getter 内不能直接使用 this.本字段名 方式获取值,它会造成 Sequelize 无限循环。非本字段的可以使用。
  • 设置 set( value ) 并调用 this.setDataValue( columnName, value )
  • 虚拟字段:定义 table 并不存在的列,类似 vue-computed
    • 需要配合 DataTypes.VIRTUAL 特殊字段使用,告诉模型并没有实际的列
    • 然后配置 get 获取内容,set 作为设置值
// 数据列
password: {
  type: DataTypes.STRING,
  allowNull: false
  get() {
    const rawValue = this.getDataValue('password');
    return `**${rawValue}**`;
  },
  set(value) {
    // 赋值时进行 doCrypto 加密处理
    this.setDataValue('password', doCrypto(value));
  }
},

- Model.api

  • insert
    • build({}) 它不连接数据库,只是创建一个本地对象(非异步)。需要调用 x.save() 真正保存
    • create({values}, {options}) 返回插入的数据对象;可通过 res.xxx获取内容
      • 返回的实例包含一下方法
        • .toJSON()
        • .save/save( [{ fields: ['xxx'] }] ) 可仅保存选择的内容
          • 执行时会把所有更改同步到数据库。如:xxx.name='';xxx.set({});
        • .destroy()
        • .reload() 未调用.save()时可获取原数据
        • .increment/decrement({ key: value })
      • 它是x.build/save()的组合操作
    • bulkCreate([{values}], {options}) _批量。_返回插入的数据对象数组;可通过 res[0].xxx获取内容
  • delete
    • destroy({options}) 返回执行结果数,没有返回 0
      • { truncate: true } 删除该表所有内容
  • update
    • update({values}, {options}) 返回结果数组,没有返回 [0]
    • 执行后数据直接同步数据库,不需要再调用save()
  • select
    • findAll({options}) 返回结果对象数组,没有返回 [];可通过 rows[0].xxx获取内容
    • findByPk(number | string, {options}) 返回结果对象,没有返回 null;可通过 row.xxx获取内容
    • findOne({options}) LIMIT 1 返回结果对象,没有返回 null;可通过 row.xxx获取内容
    • findAndCountAll({options}) 返回**结果集 **{count, rows};可通过 rows[0].xxx获取内容
  • other
    • count({options}) 返回结果数 6
    • max/min/sum(string, {options}) 返回结果数 5
    • drop({options}) 删除表

- 其他 rowInstance 内容

  • 通过查询/创建等方式获取的row其实并不是直接的数据对象,而是rowInstance里面挂载这很多sequelize提供的方法
    • 通过row.columnName/.get('key')获取单列数据内容
    • 通过row.columnName='xxx'/.set('key','value'|{ object })更改数据。非直接同步需要执行save()
    • 通过row.update({})实时同步数据库更新
    • row.changed()返回[...'columnName']数据更改的列名false说明数据未更改。
      • 注意row.columnName/.set()更改得值和原始值相同返回的是false
    • row.previous()返回{key:value}更改数据所对应的原数据内容
    • row.isNewRecord返回boolean说明该记录是否为数据库内真实数据。像xx.build()创建的数据就非真实数据
    • row.get({ plain: true })/toJSON()都返回真实数据对象。
      • 注意findAll()返回的数组,所以要在数组项上才能调用
  • 获取的 rowInstance 是可以进行链式操作的;如:const row = xxx.findByPk() -> row.update/save/destroy({a: 123}) 返回的还是 row

- 查询条件 options

  • 默认下所有的列名都已经是 aaBb 命名方式了可直接使用
    • 包括 查询条件、只读取指定列、插入数据
  • options.attributes
    • 如果需要 alias 是要使用真列名更改的名称['file_name', 'fName']
      • :使用as别名后不能直接res.xxx要用res.get('xxx')获取
    • .include/.exclude 在默认获取全部字段下,再次对数据进行处理
  • 操作符 Op
    • 基本语法{ where: { id: { [Op.gte]: 10 } } }
      • 所有属性之间是通过 AND 连接
      • 可以在创建 sequelize 实例时,添加 _operatorsAliases _属性给对应操作符添加别名
    • Op.eq / ~.ne 表示 = / !=
    • ~.is / ~.not 表示 IS xxx / IS NOT xxx
    • ~.and / ~.or 事例:[Op.or]: [{ code: code }, { name: name }]
    • ~.gt / ~.gte 表示 > / >=
    • ~.lt / ~.lte 表示 > / <=
    • ~.between / ~.notBetween 表示 BETWEEN x AND x / NOT BETWEEN x AND x
    • ~.in / ~.notIn 表示 IN [1, 2] / NOT IN [1, 2]
    • ~.like / ~.notLike / ~.startsWith / ~.endsWith / ~.substrng
    • 注意: 不能传递 undefined。null(会转为 IS NULL) 和 '' 要去看情况使用。
  • options.order: [['name', 'DESC']]
  • options.offset 偏移量
  • options.limit 限制条数
  • options.group 分组
  • options.raw = true 返回源数据;有关联的返回的是 'domain.id': 'xxx'
  • options.include 关联表
    • 支持 Model | string | array;string 时为实际的表名 domain_info
    • ~.model / ~.as 使用对象方式引入别名时,这两个属性都要写
    • ~.association 可不使用 model
    • ~.where / ~.attributes
      • 配置 where 属性后会强制改为 INNER JOIN 连接,可通过设置 required: false 更改
      • 为了获取顶层的 WHERE 可使用$nested.columRealName$语法,支持多级嵌套
      • 注: where 内的条件都是子连接内进行的是 AND 的关联。
    • ~.required: false(默认) 为 LEFT OUTER JOIN 模式,设置 true 转为 INNER JOIN
    • ~.or: false 使用 OR 替换 AND
    • ~.right: false 使用 RIGHT JOIN 连接
      • 只有当 required: false 才生效
    • ~.limit

- 偏执表(删除)

  • 创建表时设置 { paranoid: true } 后记录将不会真正删除,而是用deletedAt列数据代替
    • 数据恢复使用xx.restore()函数即可
    • 如果还需要强制删除,在执行 xx.destory({ force: true }) 配置强制参数
    • 其他查询时默认是不查询软删除的数据,可通过配置 findAll({ paranoid:false })查询

- 关联查询

A.hasOne(B, { /* 参数 */ });
A.belongsTo(B, { /* 参数 */ });
A.hasMany(B, { /* 参数 */ });
A.belongsToMany(B, { through: 'C', /* 参数 */ });
  • 关联表之间定义顺序对结果是有影响的。在上述所有示例中A称为模型,而B称为目标模型。一个可对应一个/多个目标,但一个目标只能对一个
    • _foreignKey _外键及可在 hasOne/hasMany 中,也可在 belongsTo 中定义。并且也支持对象形式可指定如:type、allowNull、defaultValue 等参数
  • 一对一:hasOnebelongsTo关联一起使用;
  • 一对多:hasManybelongsTo关联一起使用;
    • A.hasMany(B, {sourceKey, foreignKey})
      • A中的记录一个对多个B中的,外键在B
      • 创建非主键字段的关联:_sourceKey _用于定义Bforeignkey 外键,所对应A中的实际字段名
    • B.belongsTo(A, {targetKey, foreignKey})
      • B中有记录值属于A,外键在B
      • 创建非主键字段的关联:_targetKey _用于定义B中 _foreignKey _外键,所对应A中的实际字段名
    • 使用非主键字段的关联 hasMany/belongsTo 都需要写上 sourceKey/targetKey/_foreignKey _并为一个值。要不然 sequelize 还是会按照自己的方式拿而外的外键去查询
  • 多对多(常用与中间表):两个belongsToMany(, {through})调用一起使用;
    • { through: 'xx' }为关联的中间表**(必填项)**
    • 注意:实际关联表并不需要在include:{}内出现,它只是起到中间关联的作用
  • options:{ sourceKey, targetKey, foreignKey, as }
    • as 别名:当一个模型需要定义两个不同的关联时使用。as 可直接代替 foreignKey ,也可同时使用。
      • 定义后在 include 时只能使用别名 as
      • hasOne/hasMany/belongsTo 中都可定义 as,但在 hasMany 中设置 as 返回的是 []
      • 一般定义在 belongsTo 中
  • 延迟加载/预先加载
    • 延迟:指等用到了,再获取关联数据
    • 预先(include):开始查询事,直接一次获取相关关联数据
  • :关联查询出的数据是个单独的对象;
  • :默认是 outer join/ left join 方式
  • 实例关联后添加的特殊方法
    • 一对一的
      • instance.createModelB({ 数据对象 })
      • getModelB/setModelB( id/instance )
    • 一对多、多对多
      • instance.createModelB({ 数据对象 })
      • hasModelB/addModelB/removeModelB( id/instance )
      • instance.getModelBs/countModelBs/hasModelBs/addModelBs/removeModelBs/setModelBs([ id/instance ])都是复数形式
      • addModelBs/removeModelBs 是把关联表中已有数据和目标row做关联
    • 重要内容
      • .getXXX/countXXXs/hasXXXs()接受参数,可想findAll({ where: {} })类似的
      • 添加/删除方式不建议使用.setMBs(null|[2,3])。因为它只会关联id=[2,3]的记录并把该pId的其他记录关联清除。所以不清楚情况下老实用.addXXXs/removeXXXs()
      • 多对多使用.addXXX/removeXXX(rowC)会自动往中间表新增/删除两者之间的关联记录,不用手动在插入
      • 多对多.addXXX()时不太建议使用[id]方式。因为并不会判断c表中实际有无该id就进行插入,所以更建议使用[instance]方式
      • 多对多时如果中间表除了关联的外键外,还有其他的字段可能就不太合适用上面的方式了。因为没办法给它们传递内容
        • 要么就在中间表上多做个a.hasMany(c)关联,使用时a/b表手动创建。然后a.createC({ bId, 其他参数 })做中间表关联

- 事务

  • 非全局自动添加时,所有需要进入事务的都需要添加{ transaction: t }配置
  • 非托管事务
    • 创建事务并配置,然后手动执行t.commit/rollback()进操作
  • 托管事务
    • 所有增/删/改/查以及对应的关联表操作都可配置事务,只要其中一个出错全部都回滚
    • 可通过手动抛错throw new Error()强制回滚
  • 全局自动事务:配合 cls-hooked 库使用。配置Sequelize.useCLS(cls.createNamespace('_xxx_'))
    • 多个事务嵌套是默认取最近的那个事务,也可{ transaction: t1 }手动选择配置
    • 配置{ transaction: null }可跳过事务处理

- Model 同步

  • Model.sync() 如果表不存在,则创建该表(如果已经存在,则不执行任何操作)
    • force: true 将创建表。如果表已经存在,则将其首先删除
    • alter: boolean/object 这将检查数据库中表的当前状态(它具有哪些列,它们的数据类型等),然后在表中进行必要的更改以使其与模型匹配
      • alter.drop: false 防止对表内不同的内容,禁止执行任何 drop
    • match: /RegExp/ 只对匹配到的表名进行处理