From 07f10c83606e71ff59f94884a8d1402d17ff8efd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Ad=C3=A1mek?= Date: Thu, 2 May 2024 21:09:06 +0200 Subject: [PATCH] fix(core): fix hydration of relations with custom types via joined strategy This was resulting in an additional query used by the fallback select-in mechanism, so it wasn't surfacing that obviously. Closes #5518 --- packages/core/src/hydration/ObjectHydrator.ts | 2 +- packages/core/src/types/UuidType.ts | 4 ++ tests/issues/GH5518.test.ts | 72 +++++++++++++++++++ 3 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 tests/issues/GH5518.test.ts diff --git a/packages/core/src/hydration/ObjectHydrator.ts b/packages/core/src/hydration/ObjectHydrator.ts index 75a239c828c5..3cb45dbff0ce 100644 --- a/packages/core/src/hydration/ObjectHydrator.ts +++ b/packages/core/src/hydration/ObjectHydrator.ts @@ -174,7 +174,7 @@ export class ObjectHydrator extends Hydrator { if (prop.customType?.ensureComparable(meta, prop)) { context.set(`convertToDatabaseValue_${this.safeKey(prop.name)}`, (val: any) => prop.customType!.convertToDatabaseValue(val, this.platform, { mode: 'hydration' })); - ret.push(` if (data${dataKey} != null && convertCustomTypes) {`); + ret.push(` if (data${dataKey} != null && typeof data${dataKey} !== 'object' && convertCustomTypes) {`); ret.push(` data${dataKey} = convertToDatabaseValue_${this.safeKey(prop.name)}(entity${entityKey}.__helper.getPrimaryKey());`); ret.push(` }`); } diff --git a/packages/core/src/types/UuidType.ts b/packages/core/src/types/UuidType.ts index 84e83b5bdb3b..af372d490b75 100644 --- a/packages/core/src/types/UuidType.ts +++ b/packages/core/src/types/UuidType.ts @@ -12,4 +12,8 @@ export class UuidType extends Type { return 'string'; } + override ensureComparable(): boolean { + return false; + } + } diff --git a/tests/issues/GH5518.test.ts b/tests/issues/GH5518.test.ts new file mode 100644 index 000000000000..777e2f0932a3 --- /dev/null +++ b/tests/issues/GH5518.test.ts @@ -0,0 +1,72 @@ +import { Collection, Entity, ManyToOne, MikroORM, OneToMany, PrimaryKey, Property, Ref, types } from '@mikro-orm/sqlite'; +import { mockLogger } from '../helpers'; + +abstract class BaseEntity { + + @PrimaryKey({ + type: types.uuid, + onCreate: () => crypto.randomUUID(), + }) + id!: string; + +} + +@Entity() +class Product extends BaseEntity { + + @Property() + name!: string; + + @Property() + price!: number; + +} + +@Entity() +class Order extends BaseEntity { + + @OneToMany(() => OrderItem, e => e.order) + items!: Collection; + +} + + +@Entity() +class OrderItem { + + @ManyToOne(() => Order, { primary: true, ref: true }) + order!: Ref; + + @ManyToOne(() => Product, { primary: true, ref: true }) + product!: Ref; + +} + +let orm: MikroORM; + +beforeAll(async () => { + orm = await MikroORM.init({ + dbName: ':memory:', + entities: [Product, Order, OrderItem], + }); + await orm.schema.refreshDatabase(); +}); + +afterAll(async () => { + await orm.close(true); +}); + +test('basic CRUD example', async () => { + const product = orm.em.create(Product, { name: 'Product 1', price: 100 }); + const order = orm.em.create(Order, {}); + order.items.add(orm.em.create(OrderItem, { order, product })); + await orm.em.flush(); + orm.em.clear(); + + const mock = mockLogger(orm); + const [item] = await orm.em.find(OrderItem, { order: order.id }, { + populate: ['product'], + }); + expect(item.product.isInitialized()).toBe(true); + expect(mock).toHaveBeenCalledTimes(1); +});