我应该在哪里初始化 pg-promise

Posted

技术标签:

【中文标题】我应该在哪里初始化 pg-promise【英文标题】:Where should I initialize pg-promise 【发布时间】:2016-03-26 18:24:27 【问题描述】:

我刚开始学习nodejs-postgres,发现了pg-promise包。 我阅读了文档和示例,但我不明白应该将初始化代码放在哪里?我使用 Express,我有很多路线。

我必须将整个初始化(包括pg-monitor init)放到我想查询数据库的每个文件中,或者我需要在 server.js 中包含 initalize/configure 它们?

如果我只在 server.js 中初始化它们,我应该在哪些文件中包含需要数据库查询的其他文件?

换句话说。我不清楚 pg-promise 和 pg-monitor configuration/initalization 是全局操作还是本地操作?

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

var db = pgp(connection);

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

【问题讨论】:

@vitaly-t 在他的 DEMO 设置中很好地涵盖了它github.com/vitaly-t/pg-promise-demo/blob/master/javascript/… 看看 【参考方案1】:

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

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
;

查看支持的Initialization Options。

UPDATE-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

UPDATE-2

一些开发人员报告 (issue #175) 某些框架,如 NextJS 设法以打破单例模式的方式加载模块,这导致数据库模块加载多次,并产生 duplicate database警告,即使从 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 绝对不是!见Library de-initialization @vitaly-t 我只是按照上面的方法。有一个帮助目录,我在其中创建连接并将其导出并跨模块使用。在第一次之后,无论它使用该导出对象的每个模块,它都会引发此警告。 点赞!但是除了 pg-promise-demo 结构github.com/vitaly-t/pg-promise-demo 之外,您如何添加一个强大的侦听器,根据演示,我在 index.js 文件的 db 文件夹中获取了我的 db 和 pgp,但是我的一个表确实通知了哪个我需要继续收听,当我有一个除了 db/index.js 结构之外具有永久连接的强大监听器时,我会收到此警告 @PirateApp 在侦听器连接中添加额外的细节,或为主连接指定dc,因此它们被认为是不同的。【参考方案2】:

pgp 中的“连接”实际上是多个连接的自动管理池。每次您发出请求时,都会从池中抓取一个连接,打开、使用、然后关闭并返回池中。这就是为什么 vitaly-t 如此重视只为整个应用程序创建一个 pgp 实例的重要原因。结束连接的唯一原因是,如果您确实使用完数据库,即您正在优雅地关闭您的应用程序。

【讨论】:

这不是警告存在的原因。原因是因为 1)它是一种设计反模式,使用相同的连接重新初始化数据库 2)每个数据库对象分别接收和处理其可扩展性。见事件extend,会导致协议不一致。

以上是关于我应该在哪里初始化 pg-promise的主要内容,如果未能解决你的问题,请参考以下文章

我应该在凤凰城哪里调用初始化代码,这可能涉及到数据库的连接?

在 React Flux 上,我应该在哪里填充我的 Store 的初始状态?

如果不在 viewDidLoad 中,我应该在哪里进行初始网络调用(例如,最初填充提要)?

我应该在哪里存储我的 Xcode Swift 应用程序的初始数据?

在 pg-promise API 中动态选择数据库/表

QGLWidget paintEvent:在哪里初始化openGL?