Skip to content

Commit

Permalink
fix: ManyToMany ER_DUP_ENTRY error (#10343)
Browse files Browse the repository at this point in the history
* fix: ensure comparing EntityMapIds in `buildForSubjectRelation`

I have found the issue (for me at least). In the method `buildForSubjectRelation` of `ManyToManySubjectBuilder`, in order to know if it is needed to insert the relation rows `databaseRelatedEntityIds` is compared with `relatedEntityRelationIdMap` (line 154). The problem is that `databaseRelatedEntityIds` is not always an "EntityIdMap". If one is overloading the entity in the `afterLoad` event of an EventSubscriber for example the line 102 :
`const databaseRelatedEntityValue = relation.getEntityValue(subject.databaseEntity);` will return an "enhanced" object with additional properties. To fix this, in this commit, we ensure that `databaseRelatedEntityIds` is containing an "EntityIdMap".

Closes #5704

* refactor:  describe.only -> describe

removes describe.only.

* style: npm run format

format code.

* Update issue-5704.ts

refactor: typo on post title

---------

Co-authored-by: blemoine <services+bitbucket@blemoine.pro>
  • Loading branch information
growms and blemoine committed Sep 30, 2023
1 parent b8af97a commit e296063
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 2 deletions.
11 changes: 9 additions & 2 deletions src/persistence/subject-builder/ManyToManySubjectBuilder.ts
Expand Up @@ -98,10 +98,17 @@ export class ManyToManySubjectBuilder {

// if subject don't have database entity it means all related entities in persisted subject are new and must be bind
// and we don't need to remove something that is not exist
if (subject.databaseEntity)
databaseRelatedEntityIds = relation.getEntityValue(
if (subject.databaseEntity) {
const databaseRelatedEntityValue = relation.getEntityValue(
subject.databaseEntity,
)
if (databaseRelatedEntityValue) {
databaseRelatedEntityIds = databaseRelatedEntityValue.map(
(e: any) =>
relation.inverseEntityMetadata.getEntityIdMap(e),
)
}
}

// extract entity's relation value
// by example: categories inside our post (subject.entity is post)
Expand Down
21 changes: 21 additions & 0 deletions test/github-issues/5704/entity/Category.ts
@@ -0,0 +1,21 @@
import {
Column,
ManyToMany,
Entity,
PrimaryGeneratedColumn,
} from "../../../../src"
import { Post } from "./Post"

@Entity()
export class Category {
@PrimaryGeneratedColumn()
id: number

@Column()
name: string

@ManyToMany(() => Post, (o) => o.categories)
posts!: Promise<Post[]>

addedProp: boolean = false
}
23 changes: 23 additions & 0 deletions test/github-issues/5704/entity/Post.ts
@@ -0,0 +1,23 @@
import {
Column,
Entity,
JoinTable,
ManyToMany,
PrimaryGeneratedColumn,
} from "../../../../src"
import { Category } from "./Category"

@Entity()
export class Post {
@PrimaryGeneratedColumn()
id: number

@Column()
title: string

@ManyToMany(() => Category, (o) => o.posts, {
cascade: true,
})
@JoinTable()
categories!: Promise<Category[]>
}
66 changes: 66 additions & 0 deletions test/github-issues/5704/issue-5704.ts
@@ -0,0 +1,66 @@
import "reflect-metadata"
import {
closeTestingConnections,
createTestingConnections,
reloadTestingDatabases,
} from "../../utils/test-utils"
import { DataSource } from "../../../src"
import { Post } from "./entity/Post"
import { Category } from "./entity/Category"
import { assert } from "chai"

describe("github issues > #5704 Many-to-many gives error ER_DUP_ENTRY everytime I save. This one also related to inverseJoinColumn.", () => {
let connections: DataSource[]
before(
async () =>
(connections = await createTestingConnections({
entities: [__dirname + "/entity/*{.js,.ts}"],
subscribers: [__dirname + "/subscriber/*{.js,.ts}"],
enabledDrivers: ["mysql"],
})),
)
beforeEach(() => reloadTestingDatabases(connections))
after(() => closeTestingConnections(connections))

it("should work as expected", () =>
Promise.all(
connections.map(async (connection) => {
const postName = "post for issue #5704"
const catName = "cat for issue #5704"

let post1 = await connection.manager.findOne(Post, {
where: { title: postName },
})

let category1 = await connection.manager.findOne(Category, {
where: { name: catName },
})

if (!category1) {
category1 = new Category()
category1.name = catName
await connection.manager.save(category1)
}

if (!post1) {
post1 = new Post()
post1.title = postName
post1.categories = Promise.resolve([category1])
await connection.manager.save(post1)
}

const categoryTest = await connection.manager.findOne(
Category,
{
where: { name: catName },
},
)
assert.isTrue(categoryTest instanceof Category)

post1.categories = Promise.resolve([categoryTest as Category])

// This is the line that causes the error "QueryFailedError: ER_DUP_ENTRY: Duplicate entry '1-1' for key 'PRIMARY'" with previous code
await connection.manager.save(post1)
}),
))
})
13 changes: 13 additions & 0 deletions test/github-issues/5704/subscriber/CategorySubscriber.ts
@@ -0,0 +1,13 @@
import { Category } from "../entity/Category"
import { EntitySubscriberInterface, EventSubscriber } from "../../../../src"

@EventSubscriber()
export class CategorySubscriber implements EntitySubscriberInterface<Category> {
listenTo() {
return Category
}

async afterLoad(entity: Category): Promise<any> {
entity.addedProp = true
}
}

0 comments on commit e296063

Please sign in to comment.