bookshelf是一个JS库,用于处理数据库请求,原理是ORM(对象关系映射)。
ORM是一种将关系型数据库对象化的方法,可以屏蔽底层异构的数据库类型。
和关系型数据库的实体关系类似,bookshelf的models之间的关系有一对一,一对多,多对多。
一对一的关系通过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);
}
});
一对多的关系通过belongsTo
,hasMany
,morphMany
/morphTo
,through
定义。
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);
}
});
多对多的关系通过belongsToMany
,through
定义。
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);
}
});
这种关系下,一个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);
}
});
列名最好是用下划线的格式命名。
bookshelf对象的实例化,需要传入一个knex实例作为参数。
new Bookshelf(knex)
bookshelf.knex
返回被引用的knex实例
bookshelf.transaction(transactionCallback)
返回事务处理回调函数的Promise实例
model通过指定表名以及与其他model的关系来表示单个数据库表(原文为individual database rows,不知道怎么翻译更好),可以通过特定的方法扩展。
new Model (attributes, [options])
attributes,用于初始化model的属性
[options],指定表名,时间戳,解析器等
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的函数
model.hasTimestamps
用于设置created_at
和updated_at
属性,如果要修改默认的列名,则传入一个数组,第一个元素用于替换created_at
,第二个元素用于替换updated_at
hasTimestamps: ['createdAt', 'updatedAt']
model.idAttribute
用于指定唯一键
model.tableName
用于指定model对应的数据库表名,不可缺省
官方文档仅仅按字母顺序罗列了所有的方法,我按照各个方法的用途大致将其归类如下:
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])
model.count([column], [options])
column默认为*
对查询到的记录计数。
需要在query之后调用。
model.destroy([options])
用于执行delete
操作,用model的主键作为删除的约束条件。
会触发destroying
和destroyed
事件。
[options]
Model.NoRowsDeletedError
例子:
// delete from `User` where id = 1
new User({id: 1})
.destroy()
.then(function(model) {
// ...
});
model.fetch([options])
用于执行select
操作,任何已经设置过的属性都可以作为约束条件。
会触发fetching
和fetched
事件。
[options]
NotFoundError
例如
// 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)
约束操作范围
model.on()
model.off()
model.once(nameOrNames, callback)
model.trigger()
model.triggerThen(name, […args])
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。
model.clone()
返回一个与model完全一样的新实例(所有属性,关系都相同)。
model.isNew()
用于检测model的id,来判断该model是否是新定义的。
model.related(name)
name表示要获取的关系
返回这个model定义的方法所指明的特定的关系,如果不存在则返回undefined。
model.save([key], [val], [attrs], [options])
用于对属性执行insert
或update
操作。
如果,在参数中设置{patch:true}
则只会进行更新操作。
这个过程会有几种事件类型
insert的过程中触发creating、saving、created、saved
update的过程中触发updating、saving、updated、saved
model.timestamp([options])
给model添加时间戳属性
继承自Model和Collection
event.off(name) //解除事件监听器
event.on(name,callback) //添加事件监听器
event.once(name,callback) //添加一次性事件监听器,触发一次后就销毁
event.triggle(name,[...args]) //用于触发事件
event.triggleThen(name,[...args]) //以Promise的方式用于触发事件,有事件失败则全部失败。
bookshelf是基于Knex库构建的,许多语法需要参考knex。
其中最核心的部分就是请求构造器。