1. sequelize 多数据源使用背景
由于公司项目要将数据库进行优化更新,于是技术调研和选型选择了 PostgreSQL
数据库 和 Sequelize
框架,原本的项目使用的是mysql
数据库,直接使用mysql
自带的pool
写的,就是直接写sql
,也是多数据源,有多少个数据库就使用多少个连接池,这种动态切换没啥技术含量,直接弄个cache
用哪个就取哪个。
现在使用的Sequelize
是ORM
框架,之前的方案也是可以用的,是通过模型与数据库进行交互,多个连接使用同一个模型,那么进行增删改的时候是所有的连接都发生了增删改,问题就出现在这里,接下来开始探讨解决方案。
2. 解决方案1 之动态修改模式
上面背景提到过,我们现在使用的数据库是PostgreSQL
,是可以有多个模式的,每个模式属于不同的用户,这个不是这里主要探讨的,主要是知道可以动态切换模式就ok了。
上面的图红框框标出来的就是模式,在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
配置的关键点就是闭包,所以各位基础该打牢还是得打牢一点,不说废话,喜欢就点个赞吧!!!