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 教程。