fix: do not create junction table metadata when it already exists (#11114)

* fix: do not include already user defined junction tables in entityMetaData
This commit is contained in:
Ragy Hosny 2025-07-08 12:15:35 +02:00 committed by GitHub
parent a4c9dd8943
commit 3c26cf18a7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 149 additions and 1 deletions

View File

@ -315,7 +315,19 @@ export class EntityMetadataBuilder {
junctionEntityMetadata,
entityMetadatas,
)
entityMetadatas.push(junctionEntityMetadata)
// check if there's already a user-defined entity with the same table name
// if so, don't add the auto-generated junction entity to avoid duplicates
const hasUserDefinedTable = entityMetadatas.some(
(metadata) =>
metadata.tableName ===
junctionEntityMetadata.tableName &&
!metadata.isJunction,
)
if (!hasUserDefinedTable) {
entityMetadatas.push(junctionEntityMetadata)
}
})
})

View File

@ -0,0 +1,49 @@
import "reflect-metadata"
import {
closeTestingConnections,
createTestingConnections,
reloadTestingDatabases,
} from "../../utils/test-utils"
import { DataSource } from "../../../src/data-source/DataSource"
import { expect } from "chai"
import { documentRelationEntitySchema } from "./entity/junction-table/entities"
describe("entity-metadata-builder > build", () => {
let dataSources: DataSource[]
before(
async () =>
(dataSources = await createTestingConnections({
entities: [__dirname + "/entity/junction-table/*{.js,.ts}"],
enabledDrivers: ["sqlite"],
})),
)
beforeEach(() => reloadTestingDatabases(dataSources))
after(() => closeTestingConnections(dataSources))
it("repository has relations in metadata when user defined junction tables are used", () =>
Promise.all(
dataSources.map(async (dataSource) => {
const repository = dataSource.manager.getRepository(
documentRelationEntitySchema,
)
expect(repository.metadata.relations.length).to.be.eql(1)
}),
))
it("save doesn't throw with EntityPropertyNotFoundError when user defined junction tables are used", () =>
Promise.all(
dataSources.map(async (dataSource) => {
const repository = dataSource.manager.getRepository(
documentRelationEntitySchema,
)
await expect(
repository.find({
where: { userId: 2030 },
relations: ["document"],
}),
).to.not.be.rejected
}),
))
})

View File

@ -0,0 +1,87 @@
import { EntitySchema } from "../../../../../src/entity-schema/EntitySchema"
interface DocumentRelation {
documentRelationId: number
documentId: number
userId?: number
document?: Document
}
interface Document {
documentId: number
documentRelation?: DocumentRelation[]
}
interface User {
userId: number
documents?: Document[]
}
export const userEntitySchema = new EntitySchema<User>({
name: "user",
columns: {
userId: { type: Number, primary: true, name: "user_id" },
},
relations: {
documents: {
type: "many-to-many",
target: "document",
joinTable: {
name: "document_relation",
joinColumn: {
name: "user_id",
referencedColumnName: "userId",
},
inverseJoinColumn: {
name: "document_id",
referencedColumnName: "documentId",
},
},
},
},
})
export const documentEntitySchema = new EntitySchema<Document>({
name: "document",
columns: {
documentId: { type: Number, primary: true, name: "document_id" },
},
relations: {
documentRelation: {
type: "one-to-many",
target: "document_relation",
inverseSide: "document",
joinColumn: {
name: "document_id",
referencedColumnName: "documentId",
},
},
},
})
export const documentRelationEntitySchema = new EntitySchema<DocumentRelation>({
name: "document_relation",
columns: {
documentRelationId: {
type: Number,
primary: true,
name: "document_relation_id",
},
documentId: { type: Number, name: "document_id" },
userId: { type: Number, name: "user_id", nullable: true },
},
relations: {
document: {
type: "many-to-one",
target: "document",
inverseSide: "documentRelation",
joinColumn: {
name: "document_id",
referencedColumnName: "documentId",
},
},
},
})