Bookshelf.js 教程展示了如何使用 Bookshelf.js ORM 在 JavaScript 中对数据库进行编程。 Bookshelf.js 建立在 Knex 之上。
书架.js
Bookshelf.js 是用于 Node.js 的 JavaScript ORM,构建于 KnexSQL 查询生成器之上。它支持基于承诺的和传统的回调接口。 Bookshelf 提供事务支持、急切/嵌套急切关系加载、多态关联以及对一对一、一对多和多对多关系的支持。
Bookshelf.js 适用于 PostgreSQL、MySQL 和 SQLite3。
对象关系映射 (ORM) 是一种从面向对象语言访问关系数据库的技术。它是 Python 数据库 API 的抽象。在本文中,我们使用 PostgreSQL。
城市表
我们使用城市表。
DROP TABLE IF EXISTS cities;
CREATE TABLE cities(id serial PRIMARY KEY, name VARCHAR(255), population INT);
INSERT INTO cities(name, population) VALUES('Bratislava', 432000);
INSERT INTO cities(name, population) VALUES('Budapest', 1759000);
INSERT INTO cities(name, population) VALUES('Prague', 1280000);
INSERT INTO cities(name, population) VALUES('Warsaw', 1748000);
INSERT INTO cities(name, population) VALUES('Los Angeles', 3971000);
INSERT INTO cities(name, population) VALUES('New York', 8550000);
INSERT INTO cities(name, population) VALUES('Edinburgh', 464000);
INSERT INTO cities(name, population) VALUES('Berlin', 3671000);
设置 Bookshelf.js
我们安装 Bookshelf。
$ node -v v16.10.0
我们使用 Node 版本 16.10.0。
$ npm init -y
我们启动一个新的 Node 应用程序。
$ npm i pg $ npm i knex bookshelf
我们安装 PostgreSQL 驱动程序、Knex.js 和 Bookshelf.js。
书架计数行
在第一个示例中,我们计算cities表中的行数。
const knex = require('knex')({
client: 'pg',
connection: {
host: '127.0.0.1',
user: 'postgres',
password: '',
database: 'testdb',
charset: 'utf8'
}
});
module.exports.knex = knex;
在db.js文件中,我们定义了一个Knex客户端对象。
const knex = require('../config/db').knex;
const bookshelf = require('bookshelf')(knex);
const City = bookshelf.Model.extend({
tableName: 'cities'
});
module.exports = City;
我们有模型对象。模型对象映射到数据库表中的一行。
const knex = require('./config/db').knex;
const City = require('./model/city');
City.count().then((count) => {
console.log(`There are ${count} cities`);
}).catch((err) => {
console.log(err);
}).finally(() => {
knex.destroy();
});
该示例计算cities 表中的行数。它使用回调。
$ node count_cities.js There are 8 cities
const knex = require('./config/db').knex;
const City = require('./model/city');
async function countCities() {
try {
let count = await City.count();
console.log(`There are ${count} cities`);
} catch (e) {
logger.info(`No data found ${e}`);
} finally {
knex.destroy();
}
}
countCities();
在第二个示例中,我们将 promises 与 async/await 结合使用。
书架获取
fetch 从数据库中获取模型,使用模型上当前设置的任何属性来形成选择查询。使用 require 选项设置,如果结果为空,则返回的响应将被拒绝并返回 NotFoundError。
$ npm i winston
在此示例中,我们还使用了 Winston 日志记录模块。
const knex = require('./config/db').knex;
const City = require('./model/city');
const winston = require('winston');
const consoleTransport = new winston.transports.Console()
const options = {
transports: [consoleTransport]
}
const logger = new winston.createLogger(options)
async function fetch_city() {
try {
let val = await City.where({ 'name': 'Bratislava' }).fetch({require:true});
// console.log(val.toJSON());
logger.info(val);
} catch (e) {
logger.info(`No data found ${e}`);
} finally {
knex.destroy();
}
}
fetch_city();
该示例检索指定的城市。
let val = await City.where({ 'name': 'Bratislava' }).fetch({require:true});
我们获取一个名为“Bratislava”的模型。
logger.info(val);
我们记录返回的数据。
$ node fetch_city.js
{"message":{"id":1,"name":"Bratislava","population":432000},"level":"info"}
书架fetch_all
fetch_all 从数据库中获取模型集合,使用当前在模型上设置的任何查询参数来形成选择查询。
const knex = require('./config/db').knex;
const City = require('./model/city');
async function fetch_all() {
try {
let vals = await City.fetchAll();
console.log(vals.toJSON());
} catch (e) {
console.log(`Failed to fetch data: ${e}`);
} finally {
knex.destroy();
}
}
fetch_all();
该示例检索所有城市。
let vals = await City.fetchAll();
我们调用fetchAll函数。
console.log(vals.toJSON());
数据以JSON格式写入控制台。
$ node fetch_all.js
[ { id: 1, name: 'Bratislava', population: 432000 },
{ id: 2, name: 'Budapest', population: 1759000 },
{ id: 3, name: 'Prague', population: 1280000 },
{ id: 4, name: 'Warsaw', population: 1748000 },
{ id: 5, name: 'Los Angeles', population: 3971000 },
{ id: 6, name: 'New York', population: 8550000 },
{ id: 7, name: 'Edinburgh', population: 464000 },
{ id: 8, name: 'Berlin', population: 3671000 } ]
书架锻造帮手
Bookshelf 的forge 是一个简单的辅助函数,无需new 关键字即可实例化新模型。
const knex = require('./config/db').knex;
const City = require('./model/city');
async function fetch_city() {
try {
let val = await City.forge({ 'id': '4' }).fetch();
console.log(val.toJSON());
} catch (e) {
console.info(`No data found ${e}`);
} finally {
knex.destroy();
}
}
fetch_city();
在示例中,我们使用forge 助手选择一个城市。
书架拯救城市
使用save 保存新模型。
const knex = require('./config/db').knex;
const City = require('./model/city');
async function save_city() {
try {
let val = await City.forge({ 'name': 'Kyiv', 'population': 2884000}).save();
console.log(val.toJSON());
} catch (e) {
console.log(`Failed to save data: ${e}`);
} finally {
knex.destroy();
}
}
save_city();
该示例保存了一个新城市。
$ node save_city.js
{ name: 'Kyiv', population: 2884000, id: 9 }
$ node fetch_all.js
[ { id: 1, name: 'Bratislava', population: 432000 },
{ id: 2, name: 'Budapest', population: 1759000 },
{ id: 3, name: 'Prague', population: 1280000 },
{ id: 4, name: 'Warsaw', population: 1748000 },
{ id: 5, name: 'Los Angeles', population: 3971000 },
{ id: 6, name: 'New York', population: 8550000 },
{ id: 7, name: 'Edinburgh', population: 464000 },
{ id: 8, name: 'Berlin', population: 3671000 },
{ id: 9, name: 'Kyiv', population: 2884000 } ]
书架顺序
orderBy 函数按指定的列名和排序顺序对检索到的数据进行排序。顺序参数是可选的,默认为’ASC’。
const knex = require('./config/db').knex;
const City = require('./model/city');
async function fetch_city() {
try {
let vals = await City.forge().orderBy('name', 'DESC').fetchAll({require:true});
console.log(vals.toJSON());
} catch (e) {
console.log(`Failed to fetch data: ${e}`);
} finally {
knex.destroy();
}
}
fetch_city();
在示例中,我们获取所有城市并按名称降序排列。
$ node order_by.js
[ { id: 4, name: 'Warsaw', population: 1748000 },
{ id: 3, name: 'Prague', population: 1280000 },
{ id: 6, name: 'New York', population: 8550000 },
{ id: 5, name: 'Los Angeles', population: 3971000 },
{ id: 9, name: 'Kyiv', population: 2884000 },
{ id: 7, name: 'Edinburgh', population: 464000 },
{ id: 2, name: 'Budapest', population: 1759000 },
{ id: 1, name: 'Bratislava', population: 432000 },
{ id: 8, name: 'Berlin', population: 3671000 } ]
书架一对一关系
一对一关系是用hasOne 和belongsTo 函数定义的。
DROP TABLE IF EXISTS employees;
DROP TABLE IF EXISTS projects;
CREATE TABLE projects(id serial PRIMARY KEY, name VARCHAR(255));
INSERT INTO projects(name) VALUES('Project A');
INSERT INTO projects(name) VALUES('Project B');
INSERT INTO projects(name) VALUES('Project C');
CREATE TABLE employees(id serial PRIMARY KEY, project_id INT REFERENCES projects (id),
name VARCHAR(255));
INSERT INTO employees(project_id, name) VALUES(2, 'John Doe');
INSERT INTO employees(project_id, name) VALUES(1, 'Lucia Smith');
我们有employees 和projects。一名员工只能分配给一个项目。
书架有一个
hasOne 函数定义了模型之间的一对一关系。 hasOne 关系指定该表恰好具有另一种类型的对象,由另一个表中的外键指定。
const knex = require('../config/db').knex;
const bookshelf = require('bookshelf')(knex);
const Employee = require('./employee');
const Project = bookshelf.Model.extend({
tableName: 'projects',
employee: function () {
return this.hasOne(Employee);
}
});
module.exports = Project;
Project 模型包含hasOne 函数。通过查询项目,我们可以获取其链接的员工。
const knex = require('../config/db').knex;
const bookshelf = require('bookshelf')(knex);
const Employee = bookshelf.Model.extend({
tableName: 'employees'
});
module.exports = Employee;
这是Employee模型。
const knex = require('./config/db').knex;
const Project = require('./model/project');
async function doQuery() {
try {
let val = await Project.where({ id: 2 }).fetch({
withRelated: ['employee']
});
console.log(val.toJSON());
} catch (e) {
console.log(`Failed to fetch data: ${e}`);
} finally {
knex.destroy();
}
}
doQuery();
在示例中,我们获取一个项目及其关联的员工。
let val = await Project.where({ id: 3 }).fetch({
withRelated: ['employee']
});
指定withRelated 选项以获取集合的模型,预先加载模型上指定的任何指定关系。没有此选项,我们只能获取没有其链接员工的项目。
$ node has_one.js
{ id: 2,
name: 'Project B',
employee: { id: 1, project_id: 2, name: 'John Doe' } }
Bookshelf belongsTo 一对一
belongsTo 函数在模型是另一个目标模型的成员时使用。外键在当前(源)模型中定义。 belongsTo 函数用于一对一和one-to-many 关系。
const knex = require('../config/db').knex;
const bookshelf = require('bookshelf')(knex);
const Project = bookshelf.Model.extend({
tableName: 'projects'
});
module.exports = Project;
这是Project模型。
const knex = require('../config/db').knex;
const bookshelf = require('bookshelf')(knex);
const Project = require('./project');
const Employee = bookshelf.Model.extend({
tableName: 'employees',
project: function () {
return this.belongsTo(Project);
}
});
module.exports = Employee;
Employee 包含belongsTo函数。
const knex = require('./config/db').knex;
const Employee = require('./model/employee');
async function doQuery() {
try {
let val = await Employee.where({ id: 1 }).fetch({
withRelated: ['project'], require: true
});
console.log(val.toJSON());
} catch (e) {
console.log(`Failed to fetch data: ${e}`);
} finally {
knex.destroy();
}
}
doQuery();
在示例中,我们获取了一名员工及其链接的项目。
$ node belongs_to.js
{ id: 1,
project_id: 2,
name: 'John Doe',
project: { id: 2, name: 'Project B' } }
书架一对多关系
一对多关系是用hasMany和belongsTo函数定义的。
DROP TABLE IF EXISTS tasks;
DROP TABLE IF EXISTS users;
CREATE TABLE users(id serial PRIMARY KEY, name VARCHAR(255));
INSERT INTO users(name) VALUES('John Doe');
INSERT INTO users(name) VALUES('Lucia Smith');
CREATE TABLE tasks(id serial PRIMARY KEY, user_id INT REFERENCES users (id),
name VARCHAR(255));
INSERT INTO tasks(user_id, name) VALUES(1, 'Task A');
INSERT INTO tasks(user_id, name) VALUES(1, 'Task B');
INSERT INTO tasks(user_id, name) VALUES(1, 'Task C');
INSERT INTO tasks(user_id, name) VALUES(2, 'Task D');
INSERT INTO tasks(user_id, name) VALUES(2, 'Task E');
我们有users 和tasks。用户可以有一个或多个任务要做。单个任务只能由一个用户拥有。
书架有很多
hasMany 定义模型之间的一对多关系。该关系指定当前模型在另一个表中有一行或多行匹配此模型的主键。
const knex = require('../config/db').knex;
const bookshelf = require('bookshelf')(knex);
const Task = require('./task');
const User = bookshelf.Model.extend({
tableName: 'users',
tasks: function() {
return this.hasMany(Task);
}
});
module.exports = User;
User 模型包含hasMany 函数。
const knex = require('../config/db').knex;
const bookshelf = require('bookshelf')(knex);
const Task = bookshelf.Model.extend({
tableName: 'tasks',
});
module.exports = Task;
这是Task模型。
const knex = require('./config/db').knex;
const User = require('./model/user');
async function doQuery() {
try {
let val = await User.where({ id: 1 }).fetch({
withRelated: ['tasks'], require: true
});
console.log(val.toJSON());
} catch (e) {
console.log(`Failed to fetch data: ${e}`);
} finally {
knex.destroy();
}
}
doQuery();
在示例中,我们获取一个用户及其任务。
$ node has_many.js
{ id: 1,
name: 'John Doe',
tasks:
[ { id: 1, user_id: 1, name: 'Task A' },
{ id: 2, user_id: 1, name: 'Task B' },
{ id: 3, user_id: 1, name: 'Task C' } ] }
Id 为 1 的用户有 3 个任务。
书架属于一对多
在一对多的多重性中,belongsTo 是hasMany 的倒数并且是关联的一侧。
const knex = require('../config/db').knex;
const bookshelf = require('bookshelf')(knex);
const User = bookshelf.Model.extend({
tableName: 'users',
});
module.exports = User;
这是User模型。
const knex = require('../config/db').knex;
const bookshelf = require('bookshelf')(knex);
const User = require('./user');
const Task = bookshelf.Model.extend({
tableName: 'tasks',
user: function() {
return this.belongsTo(User);
}
});
module.exports = Task;
Task 模型包含belongsTo 函数。
const knex = require('./config/db').knex;
const Task = require('./model/task');
async function doQuery() {
try {
let val = await Task.where({ id: 4 }).fetch({
withRelated: ['user'], require: true
});
console.log(val.toJSON());
} catch (e) {
console.log(`Failed to fetch data: ${e}`);
} finally {
knex.destroy();
}
}
doQuery();
在示例中,我们获取一个任务及其关联的用户。
在本文中,我们使用了 Bookshelf 库。我们创建了一些与 PostgreSQL 交互的命令行程序。
列出所有 JavaScript 教程。
