在哪里初始化pg-promise?

28

我刚开始学习nodejs-postgres,并找到了pg-promise包。 我阅读了文档和示例,但我不知道应该把初始化代码放在哪里?我正在使用Express,并且有很多路由。

我需要将整个初始化(包括pg-monitor的初始化)放置在每个需要查询数据库的单个文件中吗?还是只需要在server.js中初始化/配置它们?

如果我只在server.js中进行了初始化,那么在我需要进行数据库查询的其他文件中应该包含什么呢?

换句话说,我不清楚pg-promise和pg-monitor的配置/初始化是全局的还是局部的操作?

是否需要为每个查询创建一个db变量并为每个查询结束pgp?

var db = pgp(connection);

db.query(...).then(...).catch(...).finally(**pgp.end**);

@vitaly-t 在他的 DEMO 设置中已经非常好地解决了这个问题。请查看 https://github.com/vitaly-t/pg-promise-demo/blob/master/JavaScript/index.js - PirateApp
2个回答

62

您只需要初始化一次数据库连接。如果要在模块之间共享,则将其放入自己的模块文件中,如下所示:

const initOptions = {
    // initialization options;
};

const pgp = require('pg-promise')(initOptions);

const cn = 'postgres://username:password@host:port/database';
const db = pgp(cn);

module.exports = {
    pgp, db
};

请查看支持的初始化选项

更新-1

如果您尝试使用相同的连接细节创建多个数据库对象,则该库将在控制台输出警告:

WARNING: Creating a duplicate database object for the same connection.
    at Object.<anonymous> (D:\NodeJS\tests\test2.js:14:6)

这说明您的数据库使用模式有问题,即您应该共享数据库对象,如上所示,而不是一遍又一遍地重新创建它。自6.x版本以来,每个数据库对象都维护其自己的连接池,因此复制这些对象还会导致连接使用不良,这变得非常关键。
此外,没有必要导出已初始化的pgp库实例。相反,您可以只执行以下操作:
module.exports = db;

如果在某个模块中需要使用库的根目录,您可以通过属性$config访问:

const db = require('../db'); // your db module
const pgp = db.$config.pgp; // the library's root after initialization

更新-2

一些开发人员报告(问题#175)称,某些框架(如NextJS)以破坏单例模式的方式加载模块,这导致数据库模块被加载超过一次,并产生“重复数据库”警告,尽管从NodeJS的角度来看它应该可以正常工作。

以下是针对此类集成问题的解决方法,通过使用Symbol将单例强制到全局范围中。让我们创建一个可重用的辅助工具来创建单例......

// generic singleton creator:
export function createSingleton<T>(name: string, create: () => T): T {
    const s = Symbol.for(name);
    let scope = (global as any)[s];
    if (!scope) {
        scope = {...create()};
        (global as any)[s] = scope;
    }
    return scope;
}

使用上述助手,你可以将 TypeScript 数据库文件修改为以下内容:
import * as pgLib from 'pg-promise';

const pgp = pgLib(/* initialization options */);

interface IDatabaseScope {
    db: pgLib.IDatabase<any>;
    pgp: pgLib.IMain;
}

export function getDB(): IDatabaseScope {
    return createSingleton<IDatabaseScope>('my-app-db-space', () => {
        return {
            db: pgp('my-connect-string'),
            pgp
        };
    });
}

然后,在任何使用数据库的文件开头,你可以这样做:

import {getDB} from './db';

const {db, pgp} = getDB();

这将确保单例模式的持久性。

谢谢,现在清楚了。那pgp.end呢?我需要把它放在每个查询的末尾吗? - ggabor
@ggabor 绝对不行!请参阅 Library de-initialization - vitaly-t
1
@vitaly-t 我刚刚按照上述方法进行了操作。我创建了一个帮助文件夹,在其中建立连接并导出它,然后在各个模块中使用它。第一次之后,每当任何模块使用该导出对象时,都会出现此警告。 - kishram
点赞!但是如何添加一个强大的监听器,以补充pg-promise-demo结构 https://github.com/vitaly-t/pg-promise-demo/,根据演示,我已经在db文件夹中的index.js文件中获得了我的db和pgp,但是我的其中一张表会发出通知,我需要继续监听。当我在db/index.js结构中添加一个永久连接的强大监听器时,我会收到警告。 - PirateApp
1
@PirateApp 在监听器连接中添加额外的细节,或者指定 dc 作为主连接,以便它们被视为不同的连接。 - vitaly-t

2

在pgp中,“连接”实际上是一个自动管理的多连接池。每次您发出请求时,将从池中抓取一个连接,打开、使用,然后关闭并返回至池中。这就是为什么vitaly-t会非常强调只为整个应用程序创建一个pgp实例的重要性的主要原因之一。唯一需要终止连接的原因是您确定已经完成了对数据库的使用,即您正在优雅地关闭您的应用程序。


这不是警告存在的原因。原因是因为1)它是一种设计反模式,使用相同的连接重新初始化数据库2)每个数据库对象单独接收和处理其可扩展性。请参见事件[extend](http://vitaly-t.github.io/pg-promise/global.html#event:extend),这可能会使协议不一致。 - vitaly-t

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接