一对一的关系通过 belongsTo hasOne morphOne 来定义。

var Book = bookshelf.Model.extend({
  tableName: 'books',
  summary: function() {
    return this.hasOne(Summary);
var Summary = bookshelf.Model.extend({
  tableName: 'summaries',
  book: function() {
    return this.belongsTo(Book);

2.2 一对多

一对多的关系通过belongsTohasManymorphMany /morphTothrough定义。

var Book = bookshelf.Model.extend({
  tableName: 'books',
  pages: function() {
    return this.hasMany(Page);
var Page = bookshelf.Model.extend({
  tableName: 'pages',
  book: function() {
    return this.belongsTo(Book);

2.3 多对多

多对多的关系通过belongsToManythrough定义。

var Book = bookshelf.Model.extend({
  tableName: 'books',
  authors: function() {
    return this.belongsToMany(Author);
var Author = bookshelf.Model.extend({
  tableName: 'authors',
  books: function() {
    return this.belongsToMany(Book);

2.4 Polymorphic(多态关系)

这种关系下,一个model可以属于多个model。

var Site = bookshelf.Model.extend({
  tableName: 'sites',
  photo: function() {
    return this.morphOne(Photo, 'imageable');
var Post = bookshelf.Model.extend({
  tableName: 'posts',
  photos: function() {
    return this.morphMany(Photo, 'imageable');
var Photo = bookshelf.Model.extend({
  tableName: 'photos',
  imageable: function() {
    return this.morphTo('imageable', Site, Post);

列名最好是用下划线的格式命名。

3 Bookshelf类

bookshelf对象的实例化,需要传入一个knex实例作为参数。

3.1 构造方法

new Bookshelf(knex)

3.2 成员

bookshelf.knex

返回被引用的knex实例

3.3 方法

bookshelf.transaction(transactionCallback)

返回事务处理回调函数的Promise实例

4 Model类

model通过指定表名以及与其他model的关系来表示单个数据库表(原文为individual database rows,不知道怎么翻译更好),可以通过特定的方法扩展。

4.1 构造方法

new Model (attributes, [options])

attributes,用于初始化model的属性
[options],指定表名,时间戳,解析器等

4.2 静态方法

Model.collection([models],[options])

实例化Collection对象,将当前model设置为collection的目标
返回Collection对象

Model.count([column],[options])

返回查找到的记录的数量。

Model.extend([prototypeProperties],[classProperties])

用于扩展bookshelf.Model类,扩展的方法都是直接定义在原型链上,子类可以继续扩展。

var checkit  = require('checkit');
var Promise  = require('bluebird');
var bcrypt   = Promise.promisifyAll(require('bcrypt'));
var Customer = bookshelf.Model.extend({
  initialize: function() {
    this.on('saving', this.validateSave);
  validateSave: function() {
    return checkit(rules).run(this.attributes);
  account: function() {
    return this.belongsTo(Account);
  login: Promise.method(function(email, password) {
    if (!email || !password) throw new Error('Email and password are both required');
    return new this({email: email.toLowerCase().trim()}).fetch({require: true}).tap(function(customer) {
      return bcrypt.compareAsync(password, customer.get('password'))
        .then(function(res) {
          if (!res) throw new Error('Invalid password');
Customer.login(email, password)
  .then(function(customer) {
    res.json(customer.omit('password'));
  }).catch(Customer.NotFoundError, function() {
    res.json(400, {error: email + ' not found'});
  }).catch(function(err) {
    console.error(err);
Model.fetchAll()

获取给定model的全部实例

Model.forge([attributes], [options])

用于实例化Model的函数

4.3 成员

model.hasTimestamps

用于设置created_atupdated_at属性,如果要修改默认的列名,则传入一个数组,第一个元素用于替换created_at,第二个元素用于替换updated_at

hasTimestamps: ['createdAt', 'updatedAt']
model.idAttribute

用于指定唯一键

model.tableName

用于指定model对应的数据库表名,不可缺省

4.4 方法

官方文档仅仅按字母顺序罗列了所有的方法,我按照各个方法的用途大致将其归类如下:

  • 用于定义关系
  • 用于操作数据库
  • 用于操作属性
  • 用于操作model
  • 用于事件处理

4.4.1 用于定义关系的方法

model.hasOne(Target, [foreignKey])

用于定义一对一的关系。
Target,用于指明要关联的model
foreignKey,用于指明Target model的外键
返回Model

model.hasMany(Target, [foreignKey])

用于定义一对多的关系。
Target,用于指明要关联的model
foreignKey,用于指明Target model的外键
返回Collection

model.belongsTo(Target, [foreignKey])

用于和hasOne以及hasMany搭配使用。
Target用于指定与之产生关联的另一个model
foreignKey用于指定外键
返回model

model.belongsToMany(Target, [table], [foreignKey], [otherKey])

用于定义多对多的关系,即当前model通过(through)其他表与一个或多个Target关联(join)。
Target,用于指明与当前model关联的model
table,用于指明相互关联的那张表
foreignKey,用于指明当前model的外键
otherKey,用于指明Target model的外键
返回Collection对象

当相互关联的那张表有主键或是其他一些信息时,可以使用through

let Doctor = bookshelf.Model.extend({
  patients: function() {
    return this.belongsToMany(Patient).through(Appointment);
let Appointment = bookshelf.Model.extend({
  patient: function() {
    return this.belongsTo(Patient);
  doctor: function() {
    return this.belongsTo(Doctor);
let Patient = bookshelf.Model.extend({
  doctors: function() {
    return this.belongsToMany(Doctor).through(Appointment);
model.morphOne(Target, [name], [columnNames], [morphValue])
model.morphTo(name, [columnNames], …Target)
model.morphMany(Target, [name], [columnNames], [morphValue])
model.through(Interim, [throughForeignKey], [otherKey])

4.4.2 用于操作数据库的方法

model.count([column], [options])

column默认为*
对查询到的记录计数。
需要在query之后调用。

model.destroy([options])

用于执行delete操作,用model的主键作为删除的约束条件。

会触发destroyingdestroyed事件。

[options]

  • [transacting] 按照事务的操作运行请求
  • [require = true],会在没有删除任何内容的情况下抛出错误 Model.NoRowsDeletedError
// delete from `User` where id = 1
new User({id: 1})
  .destroy()
  .then(function(model) {
    // ...
model.fetch([options])

用于执行select操作,任何已经设置过的属性都可以作为约束条件。

会触发fetchingfetched事件。

[options]

  • [require=false],当设置为true时,如果结果集为空,拒绝返回的响应并抛出错误NotFoundError
  • [columns=’*’],指定要取得的列(字段)
  • [transacting],按照事务的操作运行请求
  • [withRelated],要获得的关系
// select * from `books` where `ISBN-13` = '9780440180296'
new Book({'ISBN-13': '9780440180296'})
  .fetch()
  .then(function(model) {
    // outputs 'Slaughterhouse Five'
    console.log(model.get('title'));

复杂一些的例子:

let Book = bookshelf.Model.extend({
  tableName: 'books',
  editions: function() {
    return this.hasMany(Edition);
  chapters: function() {
    return this.hasMany(Chapter);
  genre: function() {
    return this.belongsTo(Genre);
new Book({'ISBN-13': '9780440180296'}).fetch({
  withRelated: [
    'genre', 'editions',
    { chapters: function(query) { query.orderBy('chapter_number'); }}
}).then(function(book) {
  console.log(book.related('genre').toJSON());
  console.log(book.related('editions').toJSON());
  console.log(book.toJSON());
model.fetchAll([options])
model.fetchPage(options)

按页获取数据库内容
pageSize为一页的记录数
page为总页数

model.load(relations, [options])
model.orderBy(sort, order)

对结果集排序
sort指定排序的标准(列名)
order指定升序(ASC)还是降序(DESC)

model.query(arguments)

用于构造请求。

model
  .query('where', 'other_id', '=', '5')
  .fetch()
  .then(function(model) {
    // ...
model
  .query({where: {other_id: '5'}, orWhere: {key: 'value'}})
  .fetch()
  .then(function(model) {
    // ...
model.query(function(qb) {
  qb.where('other_person', 'LIKE', '%Demo').orWhere('other_id', '>', 10);
}).fetch()
  .then(function(model) {
    // ...
let qb = model.query();
qb.where({id: 1}).select().then(function(resp) {
  // ...
model.resetQuery()

重置当前请求构造器的实例,会在每次数据库操作完成之后被Sync自动调用。

model.where(method)

约束操作范围

4.4.3 用于事件处理的方法

model.on()
model.off()
model.once(nameOrNames, callback)
model.trigger()
model.triggerThen(name, […args])

4.4.4 用于操作属性(attribute)的方法

model.clear()

将model的所有属性清除。
返回model

model.escape(attribute)

用于去掉属性中的html元字符。

model.format(attributes)

用于在存入数据库之前,将属性格式化。

model.get(attribute)

用于获取给定的属性的值

model.has(attribute)

用于判断给定的属性是否已经有值
true表示有
false表示null或undefined

model.hasChanged([attribute])

用于判断属性值是否被更改过
true表示自上次fetch、save或destroy之后属性被修改过
false表示没有
如果没有指定参数,则任意属性被修改过都会返回false。

model.parse(response)
model.previous(attribute)

返回给定属性的上一个值,如果没有被修改过,则返回undefined。

model.previousAttributes()

返回上次修改之前的所有属性值。

model.refresh(options)
model.serialize([options])

将属性序列化(默认会序列化成JSON格式)
默认下,参数shallow=false,会将所有相关联的对象全部JSON化(调用toJSON)

model.set(attribute, [value], [options])

给属性设置值,
如果unset=true,表示移除该属性

model.toJSON([options])

会自动调用JSON.stringify方法。

model.unset(attribute)

用于移除给定的属性,如果不存在则不进行任何操作。
返回model。

4.4.5 用于操作model的方法

model.clone()

返回一个与model完全一样的新实例(所有属性,关系都相同)。

model.isNew()

用于检测model的id,来判断该model是否是新定义的。

model.related(name)

name表示要获取的关系
返回这个model定义的方法所指明的特定的关系,如果不存在则返回undefined。

model.save([key], [val], [attrs], [options])

用于对属性执行insertupdate操作。
如果,在参数中设置{patch:true}则只会进行更新操作。

这个过程会有几种事件类型

  • “creating”
  • “updating”
  • “created”
  • “updated”
  • “saving”
  • “saved”

insert的过程中触发creating、saving、created、saved
update的过程中触发updating、saving、updated、saved

model.timestamp([options])

给model添加时间戳属性

5 Event类

继承自Model和Collection

5.1 方法

event.off(name) //解除事件监听器
event.on(name,callback) //添加事件监听器
event.once(name,callback) //添加一次性事件监听器,触发一次后就销毁
event.triggle(name,[...args]) //用于触发事件
event.triggleThen(name,[...args]) //以Promise的方式用于触发事件,有事件失败则全部失败。

6 Knex Query Builder

bookshelf是基于Knex库构建的,许多语法需要参考knex。
其中最核心的部分就是请求构造器。

bookshelf.js 在过去的三年中,我们看到了JavaScript的普及率飙升。 多年来,已经进行了多次尝试将流行语言带入服务器 。 这些尝试中最流行的是Node.js ,它是作为编写服务器应用程序的快速方法提供给社区的。 无论从性能还是在开发时间上,Node的卖点都是速度。 随着这种受欢迎程度的提高,社区得到了发展,该项目受益于更多的贡献者,从而产生了诸如Express.js的高质量模块。... 在过去的三年里,我们已经看到在JavaScript中的人气激增。 多年来,有在服用通俗的语言给了多次尝试服务器 。 最这些尝试的盛行已经Node.js的 ,这是提供给社会编写服务器应用程序的快捷方式。 该卖点节点是速度,无论是在性能方面和开发时间。 有了这种流行的社区长大,项目从更多的贡献者获益,从而导致高品质的模块,如Express.js 。 其结果是人们开始建造使用节点完整的后端。 一个后... bookshelf.js是基于knex的一个关系型数据库的ORM库。简单易用,内置了Promise的支持。这里主要罗列一些使用的例子,例子就是最好的教程。下面就是用mysql作为实例数据库表明bookshelf如何使用。其他的几个关系型数据库使用上基本一致,只是配置等地方需要使用的名称各自不同。为了更加贴近实际全部的例子都会放在Express打造的RESTful服务里。安装bookshelf和kne 术语事务指的是构成单一逻辑工作单元的操作的集合。比如:将钱从一个账户转到另一个账户就是一个事务,该事务包括分别针对每个账户的两个更新。 英文中transaction又是交易的意思,我想应该是因为事务(transaction)管理的场景首先是出现在利用银行账户进行交易(transaction)的过程中,所以计算机科学家们把数据库的这一特性称为事务(transaction)。 事务有以下几 LEFT JOIN的工作情况是这样的:你给出用来匹配两个数据表里的数据行的数据列,当来自左数据表的某个数据行与来自右数据表的某个数据行匹配时,那两个数据行的内容就会被选取为一个输出数据行;如果来自左数据表的某个数据行在右数据表里找不到匹配,它也会被选取为一个输出数据行,此时与它联结的是一个来自右数据表的“假”数据行,这个“假”数据行的所有数据列都包含NULL值。换句话说,在LEFT JOIN操作里, ////////////////////////////bookshelf and knex  bookmark //////////////// // target table sql CREATE TABLE public.company     id integer NOT NULL,     name text COLLATE pg_catalog."default" NOT N... bookshelf 详细介绍 一个基于Knex.js的Node.js ORM框架,支持PostgreSQL,MySQL和SQLite3 简单来说,Bookself是一个优秀的代码库,它易于阅读、理解、可扩展。它不强制你使用任何特定的校验scheme,而是提供灵活有效的关系或嵌套关系加载策略,一级类支持事务。它是一个精益的对象关系映射器(lean Obje...