Skip to content

Commit

Permalink
fix(core): defer cascading of persist operation
Browse files Browse the repository at this point in the history
Closes #2161
  • Loading branch information
B4nan committed Aug 29, 2021
1 parent 243f6ee commit 6abb3b0
Show file tree
Hide file tree
Showing 4 changed files with 29 additions and 3 deletions.
6 changes: 4 additions & 2 deletions packages/core/src/EntityManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -583,7 +583,8 @@ export class EntityManager<D extends IDatabaseDriver = IDatabaseDriver> {
*/
persist(entity: AnyEntity | Reference<AnyEntity> | (AnyEntity | Reference<AnyEntity>)[]): this {
if (Utils.isEntity(entity)) {
this.getUnitOfWork().persist(entity);
// do not cascade just yet, cascading of entities in persist stack is done when flushing
this.getUnitOfWork().persist(entity, new WeakSet([entity]));
return this;
}

Expand All @@ -596,7 +597,8 @@ export class EntityManager<D extends IDatabaseDriver = IDatabaseDriver> {
throw ValidationError.notDiscoveredEntity(ent, meta);
}

this.getUnitOfWork().persist(Reference.unwrapReference(ent));
// do not cascade just yet, cascading of entities in persist stack is done when flushing
this.getUnitOfWork().persist(Reference.unwrapReference(ent), new WeakSet([entity]));
}

return this;
Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/unit-of-work/UnitOfWork.ts
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,10 @@ export class UnitOfWork {
computeChangeSets(): void {
this.changeSets.clear();

for (const entity of this.persistStack) {
this.cascade(entity, Cascade.PERSIST, new WeakSet<AnyEntity>(), { checkRemoveStack: true });
}

for (const entity of this.identityMap) {
if (!this.removeStack.has(entity) && !this.orphanRemoveStack.has(entity)) {
this.persistStack.add(entity);
Expand Down
3 changes: 2 additions & 1 deletion tests/EntityManager.mongo.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1730,7 +1730,8 @@ describe('EntityManagerMongo', () => {
Object.assign(book, { tags: ['0000007b5c9c61c332380f78', tag] });
expect(book.tags).not.toBeInstanceOf(Collection);
expect(book.tags).toEqual(['0000007b5c9c61c332380f78', tag]);
expect(() => orm.em.persist(book)).toThrowError(`Entity of type BookTag expected for property Book.tags, '0000007b5c9c61c332380f78' of type string given. If you are using Object.assign(entity, data), use em.assign(entity, data) instead.`);
orm.em.persist(book);
await expect(orm.em.flush()).rejects.toThrowError(`Entity of type BookTag expected for property Book.tags, '0000007b5c9c61c332380f78' of type string given. If you are using Object.assign(entity, data), use em.assign(entity, data) instead.`);

wrap(book).assign({ tags: ['0000007b5c9c61c332380f78', tag] }, { em: orm.em });
expect(book.tags).toBeInstanceOf(Collection);
Expand Down
19 changes: 19 additions & 0 deletions tests/EntityManager.sqlite2.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1216,6 +1216,25 @@ describe('EntityManagerSqlite2', () => {
expect(res[0].tests).toHaveLength(3);
});

test('cascade persisting when persist called early (before relations are set)', async () => {
const author = orm.em.create(Author4, { name: 'a1', email: 'e1' });
orm.em.persist(author);
orm.em.assign(author, {
books: [
{ title: 't1' },
{ title: 't2' },
{ title: 't3' },
],
});

const mock = mockLogger(orm);
await orm.em.flush();
expect(mock.mock.calls[0][0]).toMatch('begin');
expect(mock.mock.calls[1][0]).toMatch('insert into `author4` (`created_at`, `email`, `name`, `terms_accepted`, `updated_at`) values');
expect(mock.mock.calls[2][0]).toMatch('insert into `book4` (`title`, `author_id`, `created_at`, `updated_at`) values');
expect(mock.mock.calls[3][0]).toMatch('commit');
});

afterAll(async () => {
await orm.close(true);
});
Expand Down

0 comments on commit 6abb3b0

Please sign in to comment.