Bookshelf.js tutorial

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 } ]

书架一对一关系

一对一关系是用hasOnebelongsTo 函数定义的。

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');

我们有employeesprojects。一名员工只能分配给一个项目。

书架有一个

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' } }

书架一对多关系

一对多关系是用hasManybelongsTo函数定义的。

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');

我们有userstasks。用户可以有一个或多个任务要做。单个任务只能由一个用户拥有。

书架有很多

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 个任务。

书架属于一对多

在一对多的多重性中,belongsTohasMany 的倒数并且是关联的一侧。

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

赞(0) 打赏

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏