Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot set embedded entity to null #3913

Closed
adamalfredsson opened this issue Mar 28, 2019 · 4 comments · Fixed by #10289 · May be fixed by #10829
Closed

Cannot set embedded entity to null #3913

adamalfredsson opened this issue Mar 28, 2019 · 4 comments · Fixed by #10289 · May be fixed by #10829

Comments

@adamalfredsson
Copy link

Issue type:

[ ] question
[x] bug report
[ ] feature request
[ ] documentation issue

Database system/driver:

[ ] cordova
[ ] mongodb
[ ] mssql
[ ] mysql / mariadb
[ ] oracle
[x] postgres
[ ] cockroachdb
[ ] sqlite
[ ] sqljs
[ ] react-native
[ ] expo

TypeORM version:

[x] latest
[ ] @next
[ ] 0.x.x (or put your version here)

Steps to reproduce or a small repository showing the problem:

Check out this small repository: https://github.com/nomadoda/typeorm-embedded-bug

I have an embedded entity:

export class Salary {
  @Column("decimal", { nullable: true })
  public amount: number;
}

And I'm using it in this entity:

@Entity()
export class Employee extends BaseEntity {
  @PrimaryGeneratedColumn()
  id: number;

  @Column(() => Salary)
  salary?: Salary | null;
}

Consider this:

createConnection()
  .then(async connection => {
    await connection.synchronize(true);
    const salary = new Salary();
    salary.amount = 120;
    const employee = Employee.create({
      salary
    });
    await employee.save();
    console.log("Saved a new employee with id: " + employee.id);

    console.log("Set salary to null and save");
    employee.salary = null;
    await employee.save();

    console.log("Get employee");
    await employee.reload();
    console.log(employee.salary);
    // Salary { amount: '120' }
  })
  .catch(error => console.log(error));

Expected outcome

Setting salary to null should set all embedded fields in database to null.

Actual outcome

Salary did not change on update

@holm
Copy link
Contributor

holm commented Feb 8, 2022

I ran into a bug today that was caused by this. It's surprising that you cannot set an embedded object to null and have all columns be null. It seems to just be ignored.

@vincentbavitz
Copy link

Seen this also. Really against my preference to collapse embedded entities to the top-level entity, but that's what I've had to do for now.

@cnfw
Copy link

cnfw commented Mar 3, 2022

As well as this, if all embedded columns are null on load, it's not possible to use the field in the top-level entity as a check.

e.g. if I had an embedded Address entity, the absence of address data can't be checked simply with entity.address. Each field in the embedded entity needs to be checked: entity.address.lineOne, entity.address.lineTwo, etc.

This isn't exactly the same problem as this issue, but I feel it's in the same realm, where it would be nice to be able to read/write null from/to the columns in the embedded entity through the top-level entity.

@khaelys
Copy link

khaelys commented Jun 20, 2023

As well as this, if all embedded columns are null on load, it's not possible to use the field in the top-level entity as a check.

e.g. if I had an embedded Address entity, the absence of address data can't be checked simply with entity.address. Each field in the embedded entity needs to be checked: entity.address.lineOne, entity.address.lineTwo, etc.

This isn't exactly the same problem as this issue, but I feel it's in the same realm, where it would be nice to be able to read/write null from/to the columns in the embedded entity through the top-level entity.

That is true and leads to a subtle bug in my case. Unfortunately, there is no transformer option on ColumnEmbeddedOptions, so I came up with this solution.

Firstly we need a function that returns null when all object properties are null.

export function asNullOnNullProperties<T>(obj: T): T | null {
  for (const key in obj) {
    if (obj[key] != null) {
      return obj;
    }
  }
  return null;
}

Now we can use some listeners to modify the embedded entity value. It depends on your use case, but this should be enough.

@AfterLoad()
@AfterInsert()
@AfterUpdate()
handleEmbeddedEntities() {
  this.myEmbeddedEntity = asNullOnNullProperties(this.myEmbeddedEntity);
}

Hope it helps 👍

test137E29B added a commit to test137E29B/typeorm that referenced this issue Apr 14, 2024
Support nullable embedded fields such that embedded documents in MongoDB can correctly be returned as null for a subdocument that is explicitly nullable.

Closes: typeorm#3913
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
7 participants