revert "feat: nullable embedded entities (#10289)" (#10614)

This reverts commit e67d7041387df78c69599c1d3c880389a935ffbf.
This commit is contained in:
Kobe Kwanten 2024-01-08 18:20:36 +01:00 committed by GitHub
parent c22e30f1d2
commit 15de46fd5d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 18 additions and 169 deletions

View File

@ -165,26 +165,3 @@ All columns defined in the `Name` entity will be merged into `user`, `employee`
This way code duplication in the entity classes is reduced.
You can use as many columns (or relations) in embedded classes as you need.
You even can have nested embedded columns inside embedded classes.
## Nullable embedded entities
When all its columns values are `null`, the embedded entity itself is considered `null`.
Saving the embedded entity as `null` will set all its columns to `null`
```typescript
export class Name {
@Column({ nullable: true })
first: string | null
@Column({ nullable: true })
last: string | null
}
const student = new Student()
student.faculty = 'Faculty'
student.name = { first: null, last: null } // same as student.name = null
await dataSource.manager.save(student)
// this will return the student name as `null`
await dataSource.getRepository(Student).findOne()
```

View File

@ -800,9 +800,6 @@ export class ColumnMetadata {
} else {
value = embeddedObject[this.propertyName]
}
} else if (embeddedObject === null) {
// when embedded object is null, set all its properties to null
value = null
}
} else {
// no embeds - no problems. Simply return column name by property name of the entity

View File

@ -762,10 +762,6 @@ export class EntityMetadata {
const relation = this.findRelationWithPropertyPath(propertyPath)
if (relation && relation.joinColumns) return relation.joinColumns
// try to find a relation with a property path being an embedded entity
const embedded = this.findEmbeddedWithPropertyPath(propertyPath)
if (embedded) return embedded.columns
return []
}

View File

@ -251,41 +251,9 @@ export class RawSqlResultsToEntityTransformer {
// we don't mark it as has data because if we will have all nulls in our object - we don't need such object
hasData = true
})
// Set all embedded column values to null if all their child columns are null
if (entity) {
metadata.embeddeds.forEach((embedded) => {
if (embedded.propertyName in entity) {
entity[embedded.propertyName] = this.deeplyNullify(
entity[embedded.propertyName],
)
}
})
}
return hasData
}
/**
* Returns whether an object is an iterrable
* This is useful when trying to avoid objects such as Dates
*/
private isIterrableObject(obj: any): obj is object {
const prototype = Object.prototype.toString.call(obj)
return prototype === "[object Object]" || prototype === "[object Array]"
}
/**
* Deeply nullify an object if all its properties values are null or undefined
*/
private deeplyNullify<T>(obj: T): T | null {
if (!this.isIterrableObject(obj)) return obj
for (const key in obj) {
obj[key] = this.deeplyNullify(obj[key] as any)
}
const nullify = Object.values(obj).every((value) => value == null)
return nullify ? null : obj
}
/**
* Transforms joined entities in the given raw results by a given alias and stores to the given (parent) entity
*/

View File

@ -33,7 +33,10 @@ describe("github issues > #300 support of embeddeds that are not set", () => {
expect(loadedRace).to.exist
expect(loadedRace!.id).to.exist
loadedRace!.name.should.be.equal("National Race")
expect(loadedRace!.duration).to.be.null
expect(loadedRace!.duration).to.exist
expect(loadedRace!.duration.minutes).to.be.null
expect(loadedRace!.duration.hours).to.be.null
expect(loadedRace!.duration.days).to.be.null
}),
))
})

View File

@ -1,31 +0,0 @@
import {
Entity,
PrimaryGeneratedColumn,
Column,
BaseEntity,
} from "../../../../src"
export class NestedEmbedded {
@Column({ type: "varchar", nullable: true })
c: string | null
}
export class Embedded {
@Column({ type: "varchar", nullable: true })
a: string | null
@Column({ type: "varchar", nullable: true })
b: string | null
@Column(() => NestedEmbedded)
nested: NestedEmbedded | null
}
@Entity()
export class Test extends BaseEntity {
@PrimaryGeneratedColumn()
id: number
@Column(() => Embedded)
embedded: Embedded | null
}

View File

@ -1,73 +0,0 @@
import "reflect-metadata"
import { expect } from "chai"
import {
closeTestingConnections,
createTestingConnections,
reloadTestingDatabases,
} from "../../utils/test-utils"
import { DataSource } from "../../../src/data-source/DataSource"
import { Test } from "./entity/Test"
describe("github issues > #3913 Cannot set embedded entity to null", () => {
let connections: DataSource[]
before(
async () =>
(connections = await createTestingConnections({
entities: [__dirname + "/entity/*{.js,.ts}"],
})),
)
beforeEach(() => reloadTestingDatabases(connections))
after(() => closeTestingConnections(connections))
it("should set all embedded columns to null when entity is set to null", () =>
Promise.all(
connections.map(async (connection) => {
const t = new Test()
t.embedded = { a: "a", b: "b", nested: { c: "c" } }
await connection.manager.save(t)
const t1 = await connection
.createQueryBuilder(Test, "s")
.getOne()
expect(t1!.embedded).to.deep.equal({
a: "a",
b: "b",
nested: { c: "c" },
})
t.embedded = null
await connection.manager.save(t)
const t2 = await connection
.createQueryBuilder(Test, "s")
.getOne()
expect(t2!.embedded).to.equal(null)
await connection
.createQueryBuilder(Test, "s")
.update()
.set({ embedded: { a: "a", b: null } })
.execute()
const t3 = await connection
.createQueryBuilder(Test, "s")
.getOne()
expect(t3!.embedded).to.deep.equal({
a: "a",
b: null,
nested: null,
})
await connection
.createQueryBuilder(Test, "s")
.update()
.set({ embedded: null })
.execute()
const t4 = await connection
.createQueryBuilder(Test, "s")
.getOne()
expect(t4!.embedded).to.equal(null)
}),
))
})

View File

@ -108,7 +108,13 @@ describe("github issues > #762 Nullable @Embedded inside @Embedded", () => {
loadedFoo3!.should.be.eql({
id: 3,
name: "Apple3",
metadata: null,
metadata: {
bar: null,
child: {
something: null,
somethingElse: null,
},
},
})
}),
))
@ -126,7 +132,13 @@ describe("github issues > #762 Nullable @Embedded inside @Embedded", () => {
loadedFoo!.should.be.eql({
id: 1,
name: "Orange",
metadata: null,
metadata: {
bar: null,
child: {
something: null,
somethingElse: null,
},
},
})
}),
))