Sequelize 多数据源配置

简介: 由于公司项目要将数据库进行优化更新,于是技术调研和选型选择了 PostgreSQL 数据库 和 Sequelize 框架,Sequelize的多数据源配置,这里提供两种解决方案,没事就进来看看吧。

1. sequelize 多数据源使用背景

由于公司项目要将数据库进行优化更新,于是技术调研和选型选择了 PostgreSQL数据库 和 Sequelize框架,原本的项目使用的是mysql数据库,直接使用mysql自带的pool写的,就是直接写sql,也是多数据源,有多少个数据库就使用多少个连接池,这种动态切换没啥技术含量,直接弄个cache用哪个就取哪个。
现在使用的SequelizeORM框架,之前的方案也是可以用的,是通过模型与数据库进行交互,多个连接使用同一个模型,那么进行增删改的时候是所有的连接都发生了增删改,问题就出现在这里,接下来开始探讨解决方案。

2. 解决方案1 之动态修改模式

上面背景提到过,我们现在使用的数据库是PostgreSQL,是可以有多个模式的,每个模式属于不同的用户,这个不是这里主要探讨的,主要是知道可以动态切换模式就ok了。

image.png

上面的图红框框标出来的就是模式,在Sequelize中,默认连接的是public模式,这里我们先实例化出一个连接,代码如下:

// sequelize.js
const {
   
   Sequelize} = require('sequelize');
const sequelize = new Sequelize({
   
   
    database: 'test', // 需要连接的数据库
    username: 'postgres', // 用户名
    password: '123456', // 密码
    host: '127.0.0.1', // IP地址
    // port: 5432, // 端口默认就是5432,可以不用配置
    dialect: 'postgres' // 选择 'mysql' | 'mariadb' | 'postgres' | 'mssql' 其一
});

module.exports = sequelize

模型中切换模式

如果不出现以外,数据库已经连上了,现在就要配置model了,上代码:

// model.js
const {
   
   Model, DataTypes} = require('sequelize');
const sequelize = require('./sequelize');
class test extends Model {
   
   
}

test.init({
   
   
    id: {
   
   
        type: DataTypes.UUID,
        primaryKey: true,
        defaultValue: DataTypes.UUIDV1,
        field: 'id',
        comment: '主键ID'
    },
    name: DataTypes.STRING
}, {
   
   
    sequelize,
    tableName: 'test',
    modelName: 'test',
});

module.exports = test

模型定义完了之后,关键点就来了,在Sequelize中有一个属性sequelize.dialect.supports.schemas,在目前v7版中,好像默认就是true,别问我为什么是好像,因为我找了很久资料没找到关于这个属性的描述,在源码中看到了的node_modules/sequelize/lib/dialects/postgres/index.js的第30行,这个属性的作用就是让你在查询的时候,查询语句变成'schemas'.'table'这样的,如果不放心可以手动设置一下这个属性为true,但是要在实例化之后设置,完了之后我们不能直接使用model,啥意思呢?直接上代码吧:

// 之前可以这样使用 model
const model = require('./model');
model.create({
   
   name: '田八'});

// 现在要这样写
const sequelize = require('./sequelize');
const model = sequelize.model('test'); // 这里对应的是model.js中的modelName属性
model.tableName.schema = 'test'; // 这里就是对应的模式名称,这一块应该封装成一个方法来使用的
model.create({
   
   name: '田八'});

这种方式我本来是不知道,也是网上查找资料的时候发现的,可以参考

解决方案2之 多链接,多数据源

因为公司的项目是多个数据库的,不是多个模式,这个架构我肯定是不会改的,因为改了又不知道要改多少代码,谁会没事去加自己的工作量呢?直接上代码吧:

// sequelize.js
const {
   
   Sequelize} = require('sequelize');
const config = {
   
   
    db_name1: {
   
   /* 这里自定义配置 */},
    db_name2: {
   
   },
    db_name3: {
   
   },
}
const connect_pool = {
   
   };

Object.entries(config).forEach(([key, value]) => {
   
   
    connect_pool[key] = new Sequelize({
   
   
        database: key,
        username: 'postgres',
        password: '123456',
        host: '127.0.0.1',
        dialect: 'postgres'
    });
});

这里的关键点还是在model中,不多说,直接上代码:

// model.js
const {
   
   Model, DataTypes} = require('sequelize');

module.exports = (sequelize) => {
   
   
    class test extends Model {
   
   
    }

    test.init({
   
   
        id: {
   
   
            type: DataTypes.UUID,
            primaryKey: true,
            defaultValue: DataTypes.UUIDV1,
            field: 'id',
            comment: '主键ID'
        },
        name: DataTypes.STRING
    }, {
   
   
        sequelize,
        tableName: 'test',
        modelName: 'test',
    });

    return test; // 这里的 return 其实要不要其实无所谓
}

这里主要使用闭包的原理进行导出,每次导出的都是一个新的model,多数据源,同时model相同可以使用这种方式(就是多个数据库表结构相同),使用的时候也很简单:

// sequelize.js
const {
   
   Sequelize} = require('sequelize');
const config = {
   
   
    db_name1: {
   
   /* 这里自定义配置 */},
    db_name2: {
   
   },
    db_name3: {
   
   },
}
const connect_pool = {
   
   };

Object.entries(config).forEach(([key, value]) => {
   
   
    connect_pool[key] = new Sequelize({
   
   
        database: key,
        username: 'postgres',
        password: '123456',
        host: '127.0.0.1',
        dialect: 'postgres'
    });
});

// 在这里直接使用就好了
const test = require('sequelize');
Object.values(connect_pool).forEach(sequelize => {
   
   
    const model = test(sequelize);
    model.create({
   
   name: '田八'});
})

优化一下

上面这种就是我现在使用的方案,当然我作为一名喜欢偷懒的程序员,肯定是要自动化注册model的,上代码:

// sequelize.js
const {
   
   Sequelize} = require('sequelize');
const config = {
   
   
    db_name1: {
   
   /* 这里自定义配置 */},
    db_name2: {
   
   },
    db_name3: {
   
   },
}
const connect_pool = {
   
   };

Object.entries(config).forEach(([key, value]) => {
   
   
    connect_pool[key] = new Sequelize({
   
   
        database: key,
        username: 'postgres',
        password: '123456',
        host: '127.0.0.1',
        dialect: 'postgres'
    });
});

// 在这里直接使用就好了
// const test = require('sequelize');
// Object.values(connect_pool).forEach(sequelize => {
   
   
//     const model = test(sequelize);
//     model.create({name: '田八'});
// })

// 优化
async function init_sequelize() {
   
   
    for (let sequelize of Object.values(connect_pool)) {
   
   
        await init_model(sequelize);
        await sequelize.sync();
    }
}

const fs = require('fs');
const path = require('path');
const init_model = (sequelize) => {
   
   
    return new Promise(resolve => {
   
   
        // 将所有的模型定义到一个目录下面,然后遍历该目录下的所有文件
        const filePath = path.resolve(__dirname, './model/');
        fs.readdir(filePath, (err, files) => {
   
   
            if (err) return;

            //遍历读取到的文件列表
            for (const filename of files) {
   
   
                // 只要js文件
                if (/.js$/.test(filename)) {
   
   
                    const init = require('./model/' + filename);
                    // 确定导出的是一个方法
                    typeof init === 'function' && init(sequelize)
                }
            }
            resolve();
        });
    })
}

init_sequelize().then(res => {
   
   
    const model = getModel('test', 'test');
    model.create({
   
   name: '田八'});
})

const getModel = (database, modelName) => {
   
   
    return connect_pool[database].model(modelName)
}

好了,任务圆满完成,这里提一个问题,曾经面试问过一个面试者一个问题,就是闭包,他直接说不常用,pass,这里model配置的关键点就是闭包,所以各位基础该打牢还是得打牢一点,不说废话,喜欢就点个赞吧!!!

目录
相关文章
|
缓存 安全 API
RPC vs. HTTP:谁主沉浮在网络通信的江湖?
RPC vs. HTTP:谁主沉浮在网络通信的江湖?
1528 0
|
资源调度
yarn配置镜像源
node学习笔记
3403 0
yarn配置镜像源
|
消息中间件 数据安全/隐私保护
RabbitMQ 清除全部队列及消息
安装RabbitMQ后可访问:http://{rabbitmq安装IP}:15672使用(默认的是帐号guest,密码guest。此账号只能在安装RabbitMQ的机器上登录,无法远程访问登录。) 远程访问登录,可以使用自己创建的帐号,给与对应的管理员权限即可。
1550 0
|
消息中间件 Web App开发 JavaScript
Node.js【简介、安装、运行 Node.js 脚本、事件循环、ES6 作业队列、Buffer(缓冲区)、Stream(流)】(一)-全面详解(学习总结---从入门到深化)
Node.js【简介、安装、运行 Node.js 脚本、事件循环、ES6 作业队列、Buffer(缓冲区)、Stream(流)】(一)-全面详解(学习总结---从入门到深化)
4036 0
|
NoSQL MongoDB 数据库
MongoDB 删除集合
10月更文挑战第14天
387 1
|
6月前
|
存储 安全 Linux
Kali Linux 2025.2 发布 (Kali 菜单焕新、BloodHound CE 和 CARsenal) - 领先的渗透测试发行版
Kali Linux 2025.2 发布 (Kali 菜单焕新、BloodHound CE 和 CARsenal) - 领先的渗透测试发行版
348 0
|
Java Maven
关于The POM for xxxxxx:jar:s missing, no dependency information avail 问题的解决
关于The POM for xxxxxx:jar:s missing, no dependency information avail 问题的解决
979 1
|
缓存 NoSQL 中间件
本地缓存Caffeine系列(二)
本地缓存Caffeine系列(二)
|
SQL JavaScript 前端开发
node.js使用Sequelize操作数据库
node.js使用Sequelize操作数据库
334 4
|
SQL 大数据 数据处理
一文搞懂连续问题
**SQL面试中,连续问题涉及窗口函数如row_number()、lag()、sum()over(order by)等,旨在测试综合能力。关键在于特定分组下,为连续内容分配相同分组ID。解题通常分为判断连续条件和后续处理两步。双排序差值法和累积求和法是常见策略。举例来说,连续登录天数、连续点击次数等题目,会在得到分组ID后用聚合函数统计分析。题目难度逐步升级,涉及销售额增长、时间间隔、涨幅条件等,要求灵活应用并处理复杂逻辑。**