likes
comments
collection
share

nestjs+typeorm动态建表及查询

作者站长头像
站长
· 阅读数 9

直接上代码说功能

midwayjs 和 nestjs 要连接数据库可以通过很多不同的 orm 库来实现,而不同的 orm 库实现动态建表肯定是不一样的,这里还是拿 typeorm 来说吧,其实和 midway 中一样,主要就是拿到 dataSource, 剩下的都是 typeorm 的事了,不管在哪个框架下,只要使用的是 typeorm,那动态建表及查询的方式都可以直接搬来用

// 这是单独的工具类文件
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 来创建新表

async createTable(name: string) {
  // 调用导入的方法
  await dynamicCreateTable(name);
  return name;
}

或者查询业务

async queryTable(name: string) {
  const repositry = await getRepositry(name);
  // 获取到 repositry 之后的用法和注入获取的使用方式是一样的
  return repositry.find();
}

在 midwayjs 的项目中,在工具类文件中获取 dataSource是通过下面这个方法获取的

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

// 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 对象。希望这篇文章能有点用的吧,迟到的答复。