Skip to content

Commit

Permalink
fix(core): merge returned values to the initial entity snapshot
Browse files Browse the repository at this point in the history
Closes #4284
  • Loading branch information
B4nan committed Apr 27, 2023
1 parent ce8447b commit e123076
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 6 deletions.
4 changes: 2 additions & 2 deletions packages/core/src/EntityManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -628,7 +628,7 @@ export class EntityManager<D extends IDatabaseDriver = IDatabaseDriver> {
convertCustomTypes: true,
});

em.unitOfWork.getChangeSetPersister().mapReturnedValues(entity, ret.row, meta);
em.unitOfWork.getChangeSetPersister().mapReturnedValues(entity, data, ret.row, meta);

if (!helper(entity).hasPrimaryKey()) {
const pk = await this.driver.findOne(meta.className, where, {
Expand Down Expand Up @@ -771,7 +771,7 @@ export class EntityManager<D extends IDatabaseDriver = IDatabaseDriver> {
convertCustomTypes: true,
});

em.unitOfWork.getChangeSetPersister().mapReturnedValues(entity, ret.rows?.[i], meta);
em.unitOfWork.getChangeSetPersister().mapReturnedValues(entity, data![i], ret.rows?.[i], meta);

if (!helper(entity).hasPrimaryKey()) {
loadPK.set(entity, allWhere[i]);
Expand Down
9 changes: 5 additions & 4 deletions packages/core/src/unit-of-work/ChangeSetPersister.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { MetadataStorage } from '../metadata';
import type { AnyEntity, Dictionary, EntityData, EntityMetadata, EntityProperty, FilterQuery, IHydrator, IPrimaryKey } from '../typings';
import type { AnyEntity, Dictionary, EntityData, EntityDictionary, EntityMetadata, EntityProperty, FilterQuery, IHydrator, IPrimaryKey } from '../typings';
import type { EntityFactory, EntityValidator, Collection } from '../entity';
import { EntityIdentifier, helper } from '../entity';
import type { ChangeSet } from './ChangeSet';
Expand Down Expand Up @@ -110,7 +110,7 @@ export class ChangeSetPersister {
this.mapPrimaryKey(meta, res.insertId as number, changeSet);
}

this.mapReturnedValues(changeSet.entity, res.row, meta);
this.mapReturnedValues(changeSet.entity, changeSet.payload, res.row, meta);
this.markAsPopulated(changeSet, meta);
wrapped.__initialized = true;
wrapped.__managed = true;
Expand Down Expand Up @@ -159,7 +159,7 @@ export class ChangeSetPersister {
this.mapPrimaryKey(meta, res.rows![i][field], changeSet);
}

this.mapReturnedValues(changeSet.entity, res.rows![i], meta);
this.mapReturnedValues(changeSet.entity, changeSet.payload, res.rows![i], meta);
this.markAsPopulated(changeSet, meta);
wrapped.__initialized = true;
wrapped.__managed = true;
Expand Down Expand Up @@ -381,7 +381,7 @@ export class ChangeSetPersister {
* No need to handle composite keys here as they need to be set upfront.
* We do need to map to the change set payload too, as it will be used in the originalEntityData for new entities.
*/
mapReturnedValues<T extends object>(entity: T, row: Dictionary | undefined, meta: EntityMetadata<T>): void {
mapReturnedValues<T extends object>(entity: T, payload: EntityDictionary<T>, row: Dictionary | undefined, meta: EntityMetadata<T>): void {
if (this.platform.usesReturningStatement() && row && Utils.hasObjectKeys(row)) {
const data = meta.props.reduce((ret, prop) => {
if (prop.fieldNames && row[prop.fieldNames[0]] != null && entity[prop.name] == null) {
Expand All @@ -393,6 +393,7 @@ export class ChangeSetPersister {

if (Utils.hasObjectKeys(data)) {
this.hydrator.hydrate(entity, meta, data as EntityData<T>, this.factory, 'returning', false, true);
Object.assign(payload, data); // merge to the changeset payload, so it gets saved to the entity snapshot
}
}
}
Expand Down
41 changes: 41 additions & 0 deletions tests/features/unit-of-work/GH4284.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Entity, OptionalProps, PrimaryKey, Property, SimpleLogger } from '@mikro-orm/core';
import { MikroORM } from '@mikro-orm/sqlite';
import { mockLogger } from '../../helpers';

@Entity()
class AEntity {

[OptionalProps]?: 'name';

@PrimaryKey()
id!: string;

@Property({ default: 'value1' })
name!: string;

}

let orm: MikroORM;

beforeAll(async () => {
orm = await MikroORM.init({
dbName: ':memory:',
entities: [AEntity],
loggerFactory: options => new SimpleLogger(options),
});
await orm.schema.createSchema();
});

afterAll(() => orm.close(true));

test(`GH issue 4284`, async () => {
orm.em.create(AEntity, { id: '1' });
const mock = mockLogger(orm);
await orm.em.flush();
await orm.em.flush();
expect(mock.mock.calls).toEqual([
['[query] begin'],
['[query] insert into `aentity` (`id`) values (\'1\') returning `id`, `name`'],
['[query] commit'],
]);
});

0 comments on commit e123076

Please sign in to comment.