5. 实例的使用、Instance类介绍

优质
小牛编辑
134浏览
2023-12-01

Instance类表示数据库中的一行记录,该类不能直接实例化,而应该由Model对象创建。Instance实例有两种,由Model.build方法创建的非持久化实例,和由Model.create方法创建的持久化实例。应该注意,Instance翻译后也叫做“实例”,但它在Sequelize 中是一个类,它的实例应该叫做“实例”类的实例。

  1. 实例的创建与使用
    • 1.1 构建非持久化实例
    • 1.2 创建持久化实例
    • 1.3 实例的更新/保存/持久化
    • 1.4 删除持久化实例
    • 1.5 批量操作
    • 1.6 实例值
    • 1.7 实例的重新加载
    • 1.8 实例字段值的增大
    • 1.9 实例字段值的减小
  2. 2. Instance类的API
    • 2.1 isNewRecord - 是否新记录
    • 2.2 Model() - 创建实例的模型
    • 2.3 sequelize() - Sequelize实例
    • 2.4 where() - 实例的查询条件
    • 2.5 getDataValue() - 获取值
    • 2.6 setDataValue() - 设置值
    • 2.7 get() - 获取值(单个或全部)
    • 2.8 set() - 设置值
    • 2.9 changed() - 判断是否修改
    • 2.10 previous() - 返回修改前的值
    • 2.11 save() - 保存实例到数据库
    • 2.12 reload() - 重新加载数据
    • 2.13 validate() - 验证属性
    • 2.14 update() - 设置并保存
    • 2.15 destroy() - 删除
    • 2.16 restore() - 数据恢复
    • 2.17 increment() - 字段值增加
    • 2.18 decrement() - 字段值减小
    • 2.19 equals() - 实例值是否相等
    • 2.20 equalsOneOf() - 实例值其中的一个相等
    • 2.21 toJSON() - 转换成JSON

1. 实例的创建与使用

1.1 构建非持久化实例

为了创建定义类的实例,需要像下面这样做。使用build方法会返回一个未保存的对象,你应该显式的调用save方法保存。

var project = Project.build({
  title: '这是一个项目',
  description: '项目的描述,来自:itbilu.com'
})
 
var task = Task.build({
  title: 'specify the project idea',
  description: 'bla',
  dea

构建的实例会自动获取我们定义的默认值:

// 先定义模型
var Task = sequelize.define('task', {
  title: Sequelize.STRING,
  rating: { type: Sequelize.STRING, defaultValue: 3 }
})
 
// build 构建一个实例
var task = Task.build({title: 'very important task'})
 
task.title  // ==> 'very important task'
task.rating // ==> 3

要将构建非持久实例保存到数据库中,需要使用use方法:

project.save().then(function() {
  // 保存后的回调
})
 
task.save().catch(function(error) {
  // mhhh, wth!
})
 
// 也可以链式的build构建实例,再you can also build, save and access the object with chaining:
Task
  .build({ title: 'foo', description: 'bar', deadline: new Date() })
  .save()
  .then(function(anotherTask) {
    // 现在可以通过anotherTask变量,访问已保存的 task
  }).catch(function(error) {
    // Ooops, 错误处理
  })

1.2 创建持久化实例

除了显式的调用save方法保存实例外,还可以create方法创建并保存实例。

Task.create({ title: 'foo', description: 'bar', deadline: new Date() }).then(function(task) {
  // 现在可以通过 task 变量访问新创建的 Task
})

1.3 实例的更新/保存/持久化

修改实例的一些值,并保存到数据库可以通过以下两种方式实现:

// 方法 1
task.title = 'a very different title now'
task.save().then(function() {})
 
// 方法 2
task.update({
  title: 'a very different title now'
}).then(function() {})

可样可以设置哪些属性需要保存,传入一个包含字段名的数组即可。

task.title = 'foooo'
task.description = 'baaaaaar'
task.save({fields: ['title']}).then(function() {
 // 现在 title 是 'foooo' 但是 description 还是和以前一样
})
 
// 同样可以使用 update 方法进行保存:
task.update({ title: 'foooo', description: 'baaaaaar'}, {fields: ['title']}).then(function() {
 // 现在 title 是 'foooo' 但是 description 还是和以前一样
})

当调用save但不做任何修改时,该方法不会做任何操作。

1.4 删除持久化实例

创建对象并获取对象引用后,可以通过destroy方法将其从数据库中删除:

Task.create({ title: 'a task' }).then(function(task) {
  // 这样删除...
  return task.destroy();
}).then(function() {
 // 删除完成 :)
})

1.5 批量操作

除更新单个实例外,还可以创建、更新和删除多个实例。批量操作的方法有:

  • Model.bulkCreate
  • Model.update
  • Model.destroy

当操作多个模型时,回调中不会返回DAO 实例。bulkCreate会一个包含多个实例/DAO的数组,但不像create,结果中没有自增属性。而updatedestroy会返回受影响的行数。

批量创建操作:

User.bulkCreate([
  { username: 'barfooz', isAdmin: true },
  { username: 'foo', isAdmin: true },
  { username: 'bar', isAdmin: false }
]).then(function() { // Notice: There are no arguments here, as of right now you'll have to...
  return User.findAll();
}).then(function(users) {
  console.log(users) // ... in order to get the array of user objects
})

更新多条数据:

Task.bulkCreate([
  {subject: 'programming', status: 'executing'},
  {subject: 'reading', status: 'executing'},
  {subject: 'programming', status: 'finished'}
]).then(function() {
  return Task.update(
    { status: 'inactive' }, /* set attributes' value */,
    { where: { subject: 'programming' }} /* where criteria */
  );
}).spread(function(affectedCount, affectedRows) {
  // .update 会返会一个包含两个值的数组,使用 .spread展开
  // 注意,只有支持 returning: true属性的数据库才会返回 affectedRows

  // affectedCount 是 2
  return Task.findAll();
}).then(function(tasks) {
  console.log(tasks) // subject为 'programming' 的对象的 status属性会更新为 'inactive'
})

删除多条数据:

Task.bulkCreate([
  {subject: 'programming', status: 'executing'},
  {subject: 'reading', status: 'executing'},
  {subject: 'programming', status: 'finished'}
]).then(function() {
  return Task.destroy({
    where: {
      subject: 'programming'
    },
    truncate: true /* 这会对表使用truncate(截断)操作,并忽略where条件 */
  });
}).then(function(affectedRows) {
  // affectedRows 是2 2
  return Task.findAll();
}).then(function(tasks) {
  console.log(tasks) // 没有 programming, 也没有 reading :(
})

如果你是从用户输入接收值,那可想要设置要插入的值。bulkCreate()接受第二个参数对象,你可以在其中传入一个fields(数组)参数,以表示哪些字段需要插入。

User.bulkCreate([
  { username: 'foo' },
  { username: 'bar', admin: true}
], { fields: ['username'] }).then(function() {
  // nope bar, you can't be admin!
})

bulkCreate是一种快速的插入数据的方式,但在插入多行数据时,我们又不希望牺牲模型验证,这时可以通过validate参数告诉Sequelize只有通过筛选的数据才能插入数据库。

var Tasks = sequelize.define('task', {
  name: {
    type: Sequelize.STRING,
    validate: {
      notNull: { args: true, msg: 'name cannot be null' }
    }
  },
  code: {
    type: Sequelize.STRING,
    validate: {
      len: [3, 10]
    }
  }
})
 
Tasks.bulkCreate([
  {name: 'foo', code: '123'},
  {code: '1234'},
  {name: 'bar', code: '1'}
], { validate: true }).catch(function(errors) {
  /* console.log(errors) would look like:
  [
    { record:
    ...
    errors:
      { name: 'SequelizeValidationError',
        message: 'Validation error',
        errors: [Object] } },
    { record:
      ...
      errors:
        { name: 'SequelizeValidationError',
        message: 'Validation error',
        errors: [Object] } }
  ]
  */
})

1.6 实例值

当我们打印实例时,会看到很多额外的值。为了隐藏这些东西,可以使用get-属性,并使用plain = true选项来返回实例值。

Person.create({
  name: 'Rambow',
  firstname: 'John'
}).then(function(john) {
  console.log(john.get({
    plain: true
  }))
})
 
// result:
 
// { name: 'Rambow',
//   firstname: 'John',
//   id: 1,
//   createdAt: Tue, 01 May 2012 19:12:16 GMT,
//   updatedAt: Tue, 01 May 2012 19:12:16 GMT
// }

1.7 实例的重新加载

实例中有一个reload方法,该方法会同步数据中的当前的数据,并使用刚加载的数据对模型属性进行重写。

Person.findOne({ where: { name: 'john' } }).then(function(person) {
  person.name = 'jane'
  console.log(person.name) // 'jane'
 
  person.reload().then(function() {
    console.log(person.name) // 'john'
  })
})

1.8 实例字段值的增大

increment方法,可以为实例属性(字段)增加一个确定义的字面值,该方法直接单个或多个字段属性值的增加。

为一个属性增加值:

User.findById(1).then(function(user) {
  return user.increment('my-integer-field', {by: 2})
}).then(/* ... */)

为多个属性增加值:

User.findById(1).then(function(user) {
  return user.increment([ 'my-integer-field', 'my-very-other-field' ], {by: 2})
}).then(/* ... */)

也可以使用对象的形式为多个属性增加值:

User.findById(1).then(function(user) {
  return user.increment({
    'my-integer-field':    2,
    'my-very-other-field': 3
  })
}).then(/* ... */)

1.9 实例字段值的减小

我们可以向increment方法传入一个负数来减小字段值。相对应的,还有一个decrement方法,该方法用于减小字段值:

为一个属性减小值:

User.findById(1).then(function(user) {
  return user.decrement('my-integer-field', {by: 2})
}).then(/* ... */)

为多个属性减小值:

User.findById(1).then(function(user) {
  return user.decrement([ 'my-integer-field', 'my-very-other-field' ], {by: 2})
}).then(/* ... */)

也可以使用对象的形式为多个属性减小值:

User.findById(1).then(function(user) {
  return user.decrement({
    'my-integer-field':    2,
    'my-very-other-field': 3
  })
}).then(/* ... */)

更多关于incrementincrement的使用请参考:

  • 单实例字段的自增、自减

2. Instance类的API

Instance类表示一个实例,表示数据库中的一行。它不能通过构造函数实例化,而应该通过Model.find*或Model.create等方法创建。

实例中包含一个dataValues属性,其中存储了实例实际所要操作的值。dataValues中的值可以通过以下几种方式访问:

instance.field
// 等价于
instance.get('field')
// 等价于
instance.getDataValue('field')

如果定义了访问器(getter)/设置器(setter),字段值从其中访问而不是从dataValues。一般会直接访问或使用get来访问属性值,而getDataValue只用于自定义的访问器。

相关

  • Sequelize#define

2.1 isNewRecord - 是否新记录

instance.isNewRecord -> Boolean

当实例是未保存到数据库的非持久化实例时,返回true

2.2 Model() - 创建实例的模型

instance.Model() -> Model

返回创建实例的Model

相关

  • Model

2.3 sequelize() - Sequelize实例

instance.sequelize() -> Sequelize

返回Sequelize实例的引用

相关

  • Sequelize

2.4 where() - 实例的查询条件

instance.where() -> Object

获取当前实例的查询条件,相当于option.where

2.5 getDataValue() - 获取值

instance.getDataValue(key) -> any

获取底层数据值。

  • key - {String},表示要获取值的字段名

2.6 setDataValue() - 设置值

instance.setDataValue(key, value)

设置底层数据值。

  • key - {String},表示要设置值的字段名
  • value - {any},表示要设置的值

2.7 get() - 获取值(单个或全部)

instance.get([key], [options]) -> Object|any

不提供key时,返回全部实例值。同样适用于虚拟访问器。

提供key时,返回字段值或返回虚拟访问器的值。

  • [key] - {String},表示要访问值的字段名
  • [options] - {Object}
  • [options.plain=false] - {Boolean},设置为true时,返回简单对象

2.8 set() - 设置值

instance.set(key, value, [options])

set用于更新实例值。set更新的值会保存在底层的dataValues对象中,如果为所设置的key设置了自定义设置器,那么设置器被调用。如果要绕过这些设置器,可以在选项中设置raw: true选项。

当通过一对象进行设置时,它会是一个循环对象,会为其中的每个key/value分别调用此方法。当设置raw时,底层的dataValues会被直接设置或扩展。

当值被修改后,修改值会被存储在的previous中,并会设置一个changed标识。

Set同样可以用于构建关联实例。当设置时,应该确认属性键能够匹配到关系实例的别名,并确认这些选项已设置关联。

在JSON/JSONB属性中,如果使用.分隔的字段,那么设置嵌套对象的值。

  • key - {String | Object},表示要设置值的字段
  • value - {any},表示要设置的值
  • [options] - {Object}
  • [options.raw=false] - {Boolean},虚拟设置器会被忽略
  • [options.reset=false] - {Boolean},清除之前的设置数据

别名:setAttributes

2.9 changed() - 判断是否修改

instance.changed([key]) -> Boolean|Array

判断字段或实例是否修改过,即判断dataValues中的值是否与_previousDataValues中的值是否相同。

当不传入参数时,会返回一个包含已修改字段的数组。当传入参数时,返回一个表示该字段是否修改的布尔值。

2.10 previous() - 返回修改前的值

instance.previous([key]) -> any|Array.<any>

返回实例修改前的值,即_previousDataValues属性中值。如果不传入参数,则返回所有已修改的值。

2.11 save() - 保存实例到数据库

instance.save([options]) -> Promise.<this|Errors.ValidationError<

较验数据,通过后持久化到数据库中。这个方法仅会保存修改过的数据,如果未发生修改那么不会进行任何操作。

操作成功会回调修改结果,验证失败则返回一个Sequelize.ValidationError对象。

  • [options] - {Object}
  • [options.fields] - {Array.<string>},可选的表示数据库中字段值,提供后仅会验证和保存其中的字段
  • [[options.silent=false]] - {Boolean},设置为 true 时,updatedAt在更新时不会发生变化
  • [options.validate=true] - {Boolean},保存前时否验证
  • [options.logging=false] - {Function},一个用于打印执行SQL的函数
  • [options.transaction=false] - {Transaction}
  • [options.searchPath=DEFAULT] - {String},指定schema的 search_path (仅 Postgres)

2.12 reload() - 重新加载数据

instance.reload([options]) -> Promise.<this>

用数据库中的数据当前实例。这不同于find(Instance.id),因为它会创建并返回一个新实例。而该方法是用新数据刷新当前实例。

  • [options] - {Object}
  • [options.logging=false] - {Function},一个用于打印执行SQL的函数

2.13 validate() - 验证属性

instance.validate([options]) -> Promise.<Errors.ValidationError|undefined>

根据模型定义的验证规则验证模型属性。验证成功时返回null,否则返回一个错误对象。

  • [options] - {Object}
  • [options.skip] - {String|Array},包含一个要跳过的验证字段的字符串或数组

2.14 update() - 设置并保存

instance.update(updates, options) -> Promise.<this>

相当于调用set方法后再调用save,但它只保存传递给它确切值,使用它更新时更原子和更安全。

  • updates - {Object},见set
  • options - {Object},见save

别名:updateAttributes

2.15 destroy() - 删除

instance.destroy([options={}]) -> Promise.<undefined>

删除实例在数据库中对应的行。设置为软删除(paranoid)时,数据行并不会真实删除,而是将deletedAt列更新为当前时间。

  • [options] - {Object}
  • [options.force=false] - {Boolean},强制删除。设置为 true时,软删除的模型也会强制删除
  • [options.logging=false] - {Function},一个用于打印执行SQL的函数
  • [options.transaction] - {Transaction}
  • [options.searchPath=DEFAULT] - {String},指定schema的 search_path (仅 Postgres)

2.16 restore() - 数据恢复

instance.restore([options={}]) -> Promise.<undefined>

恢复实例数据,仅适用于软删除(paranoid)模型

  • [options] - {Object}
  • [options.logging=false] - {Function},一个用于打印执行SQL的函数
  • [options.transaction] - {Transaction}

2.17 increment() - 字段值增加

instance.increment(fields, [options]) -> Promise<this>

为一个或多个字段增加值。这一操作在数据库中完成,也就是说它并不使用实例的存储值。其增加值使用如下语句完成:

SET column = column + X

增加后,要获取正确的实例值应该使用reload()方法重新加载数据。

  • fields - {String | Array | Object},要增加值的字段
  • [options] - {Object}
  • [options.by=1] - {Integer},要增加的数字值
  • [options.logging=false] - {Function},一个用于打印执行SQL的函数
  • [options.transaction] - {Transaction}
  • [options.searchPath=DEFAULT] - {String},指定schema的 search_path (仅 Postgres)
instance.increment('number') // 增加 1
instance.increment(['number', 'count'], { by: 2 }) // 'number' 和 'count'两个字段增加 2
instance.increment({ answer: 42, tries: 1}, { by: 2 })  // 'answer'字段增加 42, 'tries' 字段增加 1,'by' 参数将忽略,因为每一列都有自己的值

2.18 decrement() - 字段值减小

instance.decrement(fields, [options]) -> Promise<this>

为一个或多个字段减小值。这一操作在数据库中完成,也就是说它并不使用实例的存储值。其增加值使用如下语句完成:

SET column = column - X

增加后,要获取正确的实例值应该使用reload()方法重新加载数据。

  • fields - {String | Array | Object},要减小值的字段
  • [options] - {Object}
  • [options.by=1] - {Integer},要减小的数字值
  • [options.logging=false] - {Function},一个用于打印执行SQL的函数
  • [options.transaction] - {Transaction}
  • [options.searchPath=DEFAULT] - {String},指定schema的 search_path (仅 Postgres)
instance.decrement('number') // 减小 1
instance.decrement(['number', 'count'], { by: 2 }) // 'number' 和 'count'两个字段减小 2
instance.decrement({ answer: 42, tries: 1}, { by: 2 }) // 'answer'字段减小 42, 'tries' 字段减小 1, 'by' 参数将忽略,因为每一列都有自己的值

2.19 equals() - 实例值是否相等

instance.equals(other) -> Boolean

检查当前实例是否与other实例的值相等

2.20 equalsOneOf() - 实例值其中的一个相等

instance.equalsOneOf(others) -> Boolean

检查当前实例是否与others数组中的任意一个实例的值相等

2.21 toJSON() - 转换成JSON

instance.toJSON() -> object

将当前实例转换为JSON形式,意味着会从数据库中取值,并应用所有自定义的访问器。