diff --git a/packages/orm-integration/src/various.ts b/packages/orm-integration/src/various.ts index ba74a39d6..74928deb4 100644 --- a/packages/orm-integration/src/various.ts +++ b/packages/orm-integration/src/various.ts @@ -20,6 +20,25 @@ import { randomBytes } from 'crypto'; Error.stackTraceLimit = 20; export const variousTests = { + async testOneToOneCircularReferenceRelation(databaseFactory: DatabaseFactory) { + @entity.name('totocrr_inventory') + class Inventory { + id: number & PrimaryKey & AutoIncrement = 0; + constructor(public user: User & Reference) {} + } + + @entity.name('totocrr_user') + class User { + id: number & PrimaryKey & AutoIncrement = 0; + inventory: Inventory & BackReference = new Inventory(this); + } + + const database = await databaseFactory([Inventory, User]); + + const user = cast({}); + + await database.persist(user.inventory, user); + }, async testSkipDatabaseFieldForInserts(databaseFactory: DatabaseFactory) { @entity.name('test_skip_database_field_insert') class User { diff --git a/packages/sql/src/serializer/sql-serializer.ts b/packages/sql/src/serializer/sql-serializer.ts index 38636eacf..e878d5484 100644 --- a/packages/sql/src/serializer/sql-serializer.ts +++ b/packages/sql/src/serializer/sql-serializer.ts @@ -10,7 +10,7 @@ import { ContainerAccessor, - executeTemplates, + executeTemplates, isBackReferenceType, isReferenceType, isUUIDType, nodeBufferToArrayBuffer, @@ -26,7 +26,7 @@ import { TypeClass, typedArrayToBuffer, TypeObjectLiteral, - uuidAnnotation + uuidAnnotation, } from '@deepkit/type'; export const hexTable: string[] = []; @@ -96,8 +96,7 @@ function deserializeSqlArray(type: TypeArray, state: TemplateState) { * For sql databases, objects will be serialised as JSON string. */ function serializeSqlObjectLiteral(type: TypeClass | TypeObjectLiteral, state: TemplateState) { - if (undefined !== referenceAnnotation.getFirst(type)) return; - if (!isDirectPropertyOfEntity(type)) return; + if (isReferenceType(type) || isBackReferenceType(type) || !isDirectPropertyOfEntity(type)) return; //TypeClass|TypeObjectLiteral properties are serialized as JSON state.setContext({ stringify: JSON.stringify }); @@ -117,6 +116,16 @@ function deserializeSqlObjectLiteral(type: TypeClass | TypeObjectLiteral, state: serializeObjectLiteral(type, state); } +function serializeReferencedType(type: Type, state: TemplateState) { + if (type.kind !== ReflectionKind.class && type.kind !== ReflectionKind.objectLiteral) return; + // state.setContext({ isObject, isReferenceType, isReferenceHydrated }); + const reflection = ReflectionClass.from(type); + //the primary key is serialised for unhydrated references + state.template = ` + ${executeTemplates(state.fork(state.setter, new ContainerAccessor(state.accessor, JSON.stringify(reflection.getPrimary().getName()))), reflection.getPrimary().getType())} + `; +} + export class SqlSerializer extends Serializer { name = 'sql'; @@ -182,15 +191,10 @@ export class SqlSerializer extends Serializer { //for databases, types decorated with Reference will always only export the primary key. const referenceType = referenceAnnotation.registerType({ kind: ReflectionKind.class, classType: Object, types: [] }, {}); this.serializeRegistry.removeDecorator(referenceType); - this.serializeRegistry.addDecorator(isReferenceType, (type, state) => { - if (type.kind !== ReflectionKind.class && type.kind !== ReflectionKind.objectLiteral) return; - // state.setContext({ isObject, isReferenceType, isReferenceHydrated }); - const reflection = ReflectionClass.from(type); - //the primary key is serialised for unhydrated references - state.template = ` - ${executeTemplates(state.fork(state.setter, new ContainerAccessor(state.accessor, JSON.stringify(reflection.getPrimary().getName()))), reflection.getPrimary().getType())} - `; - }); + this.serializeRegistry.addDecorator(isReferenceType, serializeReferencedType); + + //for databases, types decorated with BackReference will always only export the primary key. + this.serializeRegistry.addDecorator(isBackReferenceType, serializeReferencedType); this.serializeRegistry.registerBinary((type, state) => { if (type.classType === ArrayBuffer) {