EntityMetadataNotFound:未找到 "BusinessApplication" 的元数据
EntityMetadataNotFound: No metadata for "BusinessApplication" was found
我已经使用 TypeORM 一段时间了,没有任何问题,但是在进行 API 调用时突然弹出此错误:
EntityMetadataNotFound: No metadata for "BusinessApplication" was found.
at new EntityMetadataNotFoundError (C:\Users\Robbie\Code\fit-society\node_modules\typeorm\error\EntityMetadataNotFoundError.js:10:28)
at Connection.getMetadata (C:\Users\Robbie\Code\fit-society\node_modules\typeorm\connection\Connection.js:336:19)
at EntityManager.<anonymous> (C:\Users\Robbie\Code\fit-society\node_modules\typeorm\entity-manager\EntityManager.js:459:44)
at step (C:\Users\Robbie\Code\fit-society\node_modules\tslib\tslib.js:136:27)
at Object.next (C:\Users\Robbie\Code\fit-society\node_modules\tslib\tslib.js:117:57)
at C:\Users\Robbie\Code\fit-society\node_modules\tslib\tslib.js:110:75
at new Promise (<anonymous>)
at Object.__awaiter (C:\Users\Robbie\Code\fit-society\node_modules\tslib\tslib.js:106:16)
at EntityManager.find (C:\Users\Robbie\Code\fit-society\node_modules\typeorm\entity-manager\EntityManager.js:456:24)
at module.exports../src/pages/api/business-applications/[id].ts.__webpack_exports__.default.Object (C:\Users\Robbie\Code\fit-society\.next\server\static\development\pages\api\business-applications\[id].js:1648:65)
at process._tickCallback (internal/process/next_tick.js:68:7)
调用此代码时发生:
import { BusinessApplication } from '../../../backend/all-entities';
import db from '../../../backend/database';
// in a function...
const manager = await db.getManager();
// in this case, req.data.id does equal "oldest"
const application: BusinessApplication | undefined =
req.data.id === 'oldest'
? (await manager.find(BusinessApplication, { order: { dateSubmitted: 'DESC' }, take: 1 }))[0]
: await manager.findOne(BusinessApplication, { where: { id: parseInt(req.data.id, 10) } });
if (application == null) throw createError(404, 'Business application not found');
return application;
在backend/all-entities.ts:
/**
* This file exists to solve circular dependency problems with Webpack by explicitly specifying the module loading order.
* @see https://medium.com/visual-development/how-to-fix-nasty-circular-dependency-issues-once-and-for-all-in-javascript-typescript-a04c987cf0de
*/
import Account_ from './entities/Account';
export { default as Qualification } from './entities/Qualification';
export { default as EditableAccount } from './entities/EditableAccount';
export { default as EditableBusiness } from './entities/EditableBusiness';
export { default as Business } from './entities/Business';
export { default as BusinessApplication, SendableBusinessApplication } from './entities/BusinessApplication';
export { default as EditableCustomer } from './entities/EditableCustomer';
export { default as Customer } from './entities/Customer';
export { default as Offer } from './entities/Offer';
export { default as ProductOffer } from './entities/ProductOffer';
export { default as ServiceOffer } from './entities/ServiceOffer';
在backend/database.ts:
import 'reflect-metadata';
import {
Connection,
ConnectionManager,
ConnectionOptions,
createConnection,
EntityManager,
getConnectionManager
} from 'typeorm';
import { Business, BusinessApplication, Customer, ProductOffer, ServiceOffer, Qualification } from './all-entities';
/**
* Database manager class
*/
class Database {
private connectionManager: ConnectionManager;
constructor() {
this.connectionManager = getConnectionManager();
}
private async getConnection(): Promise<Connection> {
const CONNECTION_NAME = 'default';
let connection: Connection;
if (this.connectionManager.has(CONNECTION_NAME)) {
connection = this.connectionManager.get(CONNECTION_NAME);
if (!connection.isConnected) {
connection = await connection.connect();
}
} else {
const connectionOptions: ConnectionOptions = {
name: CONNECTION_NAME,
type: 'postgres',
url: process.env.DATABASE_URL,
synchronize: true,
entities: [Business, BusinessApplication, Qualification, Customer, ProductOffer, ServiceOffer]
};
connection = await createConnection(connectionOptions);
}
return connection;
}
public getManager(): Promise<EntityManager> {
return this.getConnection().then(conn => conn.manager);
}
}
const db = new Database();
export default db;
在backend/entities/BusinessApplication.ts:
import { IsIn, IsString, IsOptional } from 'class-validator';
import { Column, CreateDateColumn, Entity, PrimaryGeneratedColumn } from 'typeorm';
import { EditableBusiness } from '../all-entities';
class PasswordlessBusinessApplication extends EditableBusiness {
@Column()
@IsIn(['individual', 'company'])
type!: 'individual' | 'company';
@Column({ nullable: true })
@IsOptional()
@IsString()
fein?: string;
@Column({ nullable: true })
@IsOptional()
@IsString()
professionalCertificationUrl?: string;
}
@Entity()
export default class BusinessApplication extends PasswordlessBusinessApplication {
@PrimaryGeneratedColumn()
id!: number;
@CreateDateColumn()
dateSubmitted!: Date;
@Column()
@IsString()
passwordHash!: string;
}
/**
* A business application sent by the client, which contains a password instead of a password hash.
* Qualification objects do not require id or business.
*/
export class SendableBusinessApplication extends PasswordlessBusinessApplication {
@IsString()
password!: string;
}
据我所知,导入都指向正确的文件,我导入了反射元数据,并将 @Entity()
装饰器放在 BusinessApplication
class 上。那么可能出了什么问题?值得注意的是,如果我将第一个文件中的 await manager.find(BusinessApplication, ...)
更改为 await manager.find('BusinessApplication', ...)
它工作正常,但我不想这样做,因为我会失去智能感知。此外,这个错误不会在服务器第一次初始化时发生,但在它被 Webpack 热模块重新加载后它会中断(这可能发生在 Next.js 处理页面之后或我更改代码之后) .
问题
对我来说,这是在 webpack 热重载之后发生的,因为当重新加载所有内容时,会生成新的实体模型。尽管生成了新的实体模型,但 TypeORM 并不知道它们,因为我只与数据库建立了一次连接,当 database.ts
模块被初始化时,正如您从文件中看到的那样。因此,当 TypeORM 比较来自 manager.find(BusinessApplication, ...)
调用的新实体和旧实体时,它说它们不相同,因为它们不具有引用相等性(在 JS 中没有两个函数是相同的)。因此,在与仅包含旧版本的 manager.connection.entityMetadatas
进行比较时未找到元数据。
修复
我只需要在每次重新加载后建立一个新的数据库连接,以便用新的实体元数据填充它。
从您的项目中删除 dist 文件夹并再次运行
当我在文件名中没有 .entity
重命名实体时出现此错误
我已经使用 TypeORM 一段时间了,没有任何问题,但是在进行 API 调用时突然弹出此错误:
EntityMetadataNotFound: No metadata for "BusinessApplication" was found.
at new EntityMetadataNotFoundError (C:\Users\Robbie\Code\fit-society\node_modules\typeorm\error\EntityMetadataNotFoundError.js:10:28)
at Connection.getMetadata (C:\Users\Robbie\Code\fit-society\node_modules\typeorm\connection\Connection.js:336:19)
at EntityManager.<anonymous> (C:\Users\Robbie\Code\fit-society\node_modules\typeorm\entity-manager\EntityManager.js:459:44)
at step (C:\Users\Robbie\Code\fit-society\node_modules\tslib\tslib.js:136:27)
at Object.next (C:\Users\Robbie\Code\fit-society\node_modules\tslib\tslib.js:117:57)
at C:\Users\Robbie\Code\fit-society\node_modules\tslib\tslib.js:110:75
at new Promise (<anonymous>)
at Object.__awaiter (C:\Users\Robbie\Code\fit-society\node_modules\tslib\tslib.js:106:16)
at EntityManager.find (C:\Users\Robbie\Code\fit-society\node_modules\typeorm\entity-manager\EntityManager.js:456:24)
at module.exports../src/pages/api/business-applications/[id].ts.__webpack_exports__.default.Object (C:\Users\Robbie\Code\fit-society\.next\server\static\development\pages\api\business-applications\[id].js:1648:65)
at process._tickCallback (internal/process/next_tick.js:68:7)
调用此代码时发生:
import { BusinessApplication } from '../../../backend/all-entities';
import db from '../../../backend/database';
// in a function...
const manager = await db.getManager();
// in this case, req.data.id does equal "oldest"
const application: BusinessApplication | undefined =
req.data.id === 'oldest'
? (await manager.find(BusinessApplication, { order: { dateSubmitted: 'DESC' }, take: 1 }))[0]
: await manager.findOne(BusinessApplication, { where: { id: parseInt(req.data.id, 10) } });
if (application == null) throw createError(404, 'Business application not found');
return application;
在backend/all-entities.ts:
/**
* This file exists to solve circular dependency problems with Webpack by explicitly specifying the module loading order.
* @see https://medium.com/visual-development/how-to-fix-nasty-circular-dependency-issues-once-and-for-all-in-javascript-typescript-a04c987cf0de
*/
import Account_ from './entities/Account';
export { default as Qualification } from './entities/Qualification';
export { default as EditableAccount } from './entities/EditableAccount';
export { default as EditableBusiness } from './entities/EditableBusiness';
export { default as Business } from './entities/Business';
export { default as BusinessApplication, SendableBusinessApplication } from './entities/BusinessApplication';
export { default as EditableCustomer } from './entities/EditableCustomer';
export { default as Customer } from './entities/Customer';
export { default as Offer } from './entities/Offer';
export { default as ProductOffer } from './entities/ProductOffer';
export { default as ServiceOffer } from './entities/ServiceOffer';
在backend/database.ts:
import 'reflect-metadata';
import {
Connection,
ConnectionManager,
ConnectionOptions,
createConnection,
EntityManager,
getConnectionManager
} from 'typeorm';
import { Business, BusinessApplication, Customer, ProductOffer, ServiceOffer, Qualification } from './all-entities';
/**
* Database manager class
*/
class Database {
private connectionManager: ConnectionManager;
constructor() {
this.connectionManager = getConnectionManager();
}
private async getConnection(): Promise<Connection> {
const CONNECTION_NAME = 'default';
let connection: Connection;
if (this.connectionManager.has(CONNECTION_NAME)) {
connection = this.connectionManager.get(CONNECTION_NAME);
if (!connection.isConnected) {
connection = await connection.connect();
}
} else {
const connectionOptions: ConnectionOptions = {
name: CONNECTION_NAME,
type: 'postgres',
url: process.env.DATABASE_URL,
synchronize: true,
entities: [Business, BusinessApplication, Qualification, Customer, ProductOffer, ServiceOffer]
};
connection = await createConnection(connectionOptions);
}
return connection;
}
public getManager(): Promise<EntityManager> {
return this.getConnection().then(conn => conn.manager);
}
}
const db = new Database();
export default db;
在backend/entities/BusinessApplication.ts:
import { IsIn, IsString, IsOptional } from 'class-validator';
import { Column, CreateDateColumn, Entity, PrimaryGeneratedColumn } from 'typeorm';
import { EditableBusiness } from '../all-entities';
class PasswordlessBusinessApplication extends EditableBusiness {
@Column()
@IsIn(['individual', 'company'])
type!: 'individual' | 'company';
@Column({ nullable: true })
@IsOptional()
@IsString()
fein?: string;
@Column({ nullable: true })
@IsOptional()
@IsString()
professionalCertificationUrl?: string;
}
@Entity()
export default class BusinessApplication extends PasswordlessBusinessApplication {
@PrimaryGeneratedColumn()
id!: number;
@CreateDateColumn()
dateSubmitted!: Date;
@Column()
@IsString()
passwordHash!: string;
}
/**
* A business application sent by the client, which contains a password instead of a password hash.
* Qualification objects do not require id or business.
*/
export class SendableBusinessApplication extends PasswordlessBusinessApplication {
@IsString()
password!: string;
}
据我所知,导入都指向正确的文件,我导入了反射元数据,并将 @Entity()
装饰器放在 BusinessApplication
class 上。那么可能出了什么问题?值得注意的是,如果我将第一个文件中的 await manager.find(BusinessApplication, ...)
更改为 await manager.find('BusinessApplication', ...)
它工作正常,但我不想这样做,因为我会失去智能感知。此外,这个错误不会在服务器第一次初始化时发生,但在它被 Webpack 热模块重新加载后它会中断(这可能发生在 Next.js 处理页面之后或我更改代码之后) .
问题
对我来说,这是在 webpack 热重载之后发生的,因为当重新加载所有内容时,会生成新的实体模型。尽管生成了新的实体模型,但 TypeORM 并不知道它们,因为我只与数据库建立了一次连接,当 database.ts
模块被初始化时,正如您从文件中看到的那样。因此,当 TypeORM 比较来自 manager.find(BusinessApplication, ...)
调用的新实体和旧实体时,它说它们不相同,因为它们不具有引用相等性(在 JS 中没有两个函数是相同的)。因此,在与仅包含旧版本的 manager.connection.entityMetadatas
进行比较时未找到元数据。
修复
我只需要在每次重新加载后建立一个新的数据库连接,以便用新的实体元数据填充它。
从您的项目中删除 dist 文件夹并再次运行
当我在文件名中没有 .entity
重命名实体时出现此错误