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

Using collection.set fails with composite foreign keys and orphan removal #3666

Closed
KevinMusgrave opened this issue Oct 27, 2022 · 3 comments
Closed
Labels
bug Something isn't working

Comments

@KevinMusgrave
Copy link

KevinMusgrave commented Oct 27, 2022

Describe the bug
Using the set function on a OneToMany attribute results in a ValidationError when the child has a composite key consisting entirely of foreign keys.

Stack trace

 ValidationError: Value for Registration.competition is required, 'null' found
    entity: Registration { user: User { id: 3 }, competition: null }

      at Function.propertyRequired (node_modules/@mikro-orm/core/errors.js:64:16)
      at EntityValidator.validateRequired (node_modules/@mikro-orm/core/entity/EntityValidator.js:49:48)
      at ChangeSetPersister.processProperties (node_modules/@mikro-orm/core/unit-of-work/ChangeSetPersister.js:77:28)
      at node_modules/@mikro-orm/core/unit-of-work/ChangeSetPersister.js:24:46
          at Array.forEach (<anonymous>)
      at ChangeSetPersister.executeInserts (node_modules/@mikro-orm/core/unit-of-work/ChangeSetPersister.js:24:20)
      at ChangeSetPersister.runForEachSchema (node_modules/@mikro-orm/core/unit-of-work/ChangeSetPersister.js:68:31)
      at ChangeSetPersister.executeInserts (node_modules/@mikro-orm/core/unit-of-work/ChangeSetPersister.js:21:25)
      at UnitOfWork.commitCreateChangeSets (node_modules/@mikro-orm/core/unit-of-work/UnitOfWork.js:704:39)

To Reproduce

The following code is mostly copied from GH519.test.ts. I changed the User class to have just an id to simplify things.

It fails to execute competition.registrations.set([registration2]).

import {
  Entity,
  PrimaryKey,
  MikroORM,
  ManyToOne,
  Collection,
  OneToMany,
} from '@mikro-orm/core';

@Entity()
class Competition {
  @PrimaryKey()
  id!: number;

  @OneToMany('Registration', 'competition', { orphanRemoval: true })
  registrations = new Collection<Registration>(this);
}

@Entity()
class User {
  @PrimaryKey()
  id!: number;
}

@Entity()
class Registration {
  @ManyToOne({ primary: true })
  competition!: Competition;

  @ManyToOne({ primary: true })
  user!: User;

  constructor(user: User, competition: Competition) {
    this.user = user;
    this.competition = competition;
  }
}

describe('GH issue 3666', () => {
  let orm;
  let em;

  beforeAll(async () => {
    orm = await MikroORM.init({
      type: 'postgresql',
      dbName: 'test',
      allowGlobalContext: true,
      entities: [Competition, User, Registration],
    });
    await orm.schema.ensureDatabase();
    await orm.schema.execute('CREATE EXTENSION IF NOT EXISTS "uuid-ossp"');
    await orm.schema.dropSchema();
    await orm.schema.createSchema();
    em = orm.em;
  });

  afterAll(async () => {
    await orm.close(true);
  });

  test(`GH issue 3666`, async () => {
    const competition = new Competition();
    const user1 = new User();
    const user2 = new User();
    const user3 = new User();
    const registration1 = new Registration(user1, competition);
    const registration2 = new Registration(user2, competition);
    const registration3 = new Registration(user3, competition);
    em.persist(registration1);
    em.persist(registration2);
    em.persist(registration3);
    await em.flush();
    let x = await em.find(Registration);
    expect(x.length).toEqual(3);

    competition.registrations.set([registration2, registration3]);
    await em.flush();
    x = await em.find(Registration);
    expect(x.length).toEqual(2);

    competition.registrations.set([registration2]);
    await em.flush();
    x = await em.find(Registration);
    expect(x.length).toEqual(1);

    em.clear();
  });
});

Expected behavior
Since competition.registrations.set([registration2, registration3]) runs, I expect competition.registrations.set([registration2]) to also run without error.

Versions

Dependency Version
node 16.14.2
typescript 4.3.5
mikro-orm 5.5.0
postgres 14.5
@KevinMusgrave
Copy link
Author

If I replace competition.registrations.set([registration2]) with either

  • competition.registrations.removeAll()
  • competition.registrations.set([])

Then there is no ValidationError, but the Registration entities are not removed. This is what I get when I console.log the Registration table after the removeAll or set([]) call.

[
  Registration {
    user: User { id: 2 },
    competition: Competition { registrations: [Collection<Registration>], id: 1 }
  },
  Registration {
    user: User { id: 3 },
    competition: Competition { registrations: [Collection<Registration>], id: 1 }
  }
]

@jrwpatterson
Copy link

I have this too I've since downgraded back to 5.4.2

@jrwpatterson
Copy link

I think its caused by this... #3543

@B4nan B4nan added the bug Something isn't working label Oct 31, 2022
@B4nan B4nan closed this as completed in 925c1d2 Oct 31, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants