之前因工作需要在 midwayjs 项目中需要使用动态建表及查询功能,所以写过一篇midway+typeorm 动态建表及查询 然后收到一条评论,问能不能发个 nestjs 项目中动态建表和查询的。这都过了三个星期了,一来是工作略忙,二来是要陪娃,所以一直也没有回复。也许留言的同学已经自己实现了相关功能了。但想着还是回复一下,所以又写了这篇文章。
直接上代码说功能
midwayjs 和 nestjs 要连接数据库可以通过很多不同的 orm 库来实现,而不同的 orm 库实现动态建表肯定是不一样的,这里还是拿 typeorm 来说吧,其实和 midway 中一样,主要就是拿到 dataSource , 剩下的都是 typeorm 的事了,不管在哪个框架下,只要使用的是 typeorm,那动态建表及查询的方式都可以直接搬来用
ts
// 这是单独的工具类文件
import { DataSource, Entity, Table } from 'typeorm';
import { ConnectionMetadataBuilder } from 'typeorm/connection/ConnectionMetadataBuilder';
import BaseEntity from './entities/base.entity';
let dataSource: DataSource;
// 设置 dataSource
export function setDataSource(d: DataSource) {
dataSource = d;
}
async function buildMetadata(entity: typeof BaseEntity) {
const [entityMetadata] = await new ConnectionMetadataBuilder(
dataSource,
).buildEntityMetadatas([entity]);
dataSource.entityMetadatas.push(entityMetadata);
dataSource.entityMetadatasMap.set(entityMetadata.target, entityMetadata);
}
const entityMap = new Map<string, typeof BaseEntity>();
// 根据 name 获取/新建一个 Entity
export async function getEntity(name: string) {
if (entityMap.has(name)) {
return entityMap.get(name);
}
const tableName = `dynamic_${name}`;
@Entity(tableName)
class DynamicEntity extends BaseEntity {}
await buildMetadata(DynamicEntity);
entityMap.set(name, DynamicEntity);
return DynamicEntity;
}
async function getMetadata(name: string) {
const entity = await getEntity(name);
return dataSource.getMetadata(entity);
}
// 动态建表
export async function dynamicCreateTable(name: string) {
const metadata = await getMetadata(name);
const runner = dataSource.createQueryRunner();
await runner.createTable(Table.create(metadata, dataSource.driver));
await runner.release();
}
// 获取 repositry 以便后续的查询等操作
export async function getRepositry(name: string) {
const entity = await getEntity(name);
return dataSource.getRepository<typeof BaseEntity>(entity);
}
在业务代码中调用 dynamicCreateTable
来创建新表
ts
async createTable(name: string) {
// 调用导入的方法
await dynamicCreateTable(name);
return name;
}
或者查询业务
ts
async queryTable(name: string) {
const repositry = await getRepositry(name);
// 获取到 repositry 之后的用法和注入获取的使用方式是一样的
return repositry.find();
}
在 midwayjs 的项目中,在工具类文件中获取 dataSource是通过下面这个方法获取的
ts
import { getCurrentApplicationContext } from '@midwayjs/core';
import { TypeORMDataSourceManager } from '@midwayjs/typeorm';
export async function getDataSource() {
const sourceManager = await getCurrentApplicationContext().getAsync(TypeORMDataSourceManager);
return sourceManager.getDataSource('default');
}
但是在 nestjs 中并未发现类似的方法,基本都是通过注入的方法获取的。但是在这个工具类文件中就是一堆函数的集合,不好使用注入。所以在 nestjs 项目中我选择了增加一个 dataSource 变量并增加了 setDataSource 方法。并在项目启动后手动调用此方法设置 dataSource
ts
// main.ts文件
// 一堆 import 没写
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const dataSource = app.get(DataSource);
// 调用从工具文件导入的设置方法
setDataSource(dataSource);
await app.listen(3000);
}
bootstrap();
这种方式怎么看都不是最优的方式,不知道在 nestjs 框架中有没有其他更好的方式,有知道的大佬可以留言告知一下,非常感谢🤝
其实我也没用过 nestjs 这个框架。平时工作主要还是前端网页那些东西,这些后端服务/数据库部分日常工作都不怎么涉及。但是如果只是动态建表及查询功能,所以和 midway 项目中实现还是基本一样的,唯一的差别就是上面说如何获取 dataSource 对象。希望这篇文章能有点用的吧,迟到的答复。