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

Persisting a side of 1:1 relationship using getReference to obtain id of another side - not working on 5.7.8 #4497

Closed
merkator95 opened this issue Jul 3, 2023 · 2 comments
Labels
bug Something isn't working

Comments

@merkator95
Copy link

Describe the bug

I've encountered an issue while trying to persist a new entity with one-to-one relationship with another, already persisted entity, using getReference() method to obtain reference to already-persisted side of 1:1 relationship.

I experienced this on update from 5.7.7 to 5.7.8.

To Reproduce

Repro repository link: https://github.com/merkator95/mikro-orm-entity-manager-get-reference-repro

Application manages two entities: Car and Driver. There is 1:1 relationship between those achieved via:

@Entity()
export class CarEntity extends CustomBaseEntity {
  @Property()
  brand: string;

  @OneToOne({ nullable: true, type: 'DriverEntity' })
  driver?: DriverEntity;
}

@Entity()
export class DriverEntity extends CustomBaseEntity {
  @OneToOne(() => CarEntity, (car) => car.driver, {
    orphanRemoval: true,
  })
  car: CarEntity;

  @Property()
  name: string;
}

a controller accepts Car brand and Car driver name as follows:

@Body() body: { brand: string; driverName: string },

A Car repository in its own class persists Car entity:

  async createCar(body: { brand: string; driverName: string }) {
    const record = new CarEntity();
    record.brand = body.brand;
    this.em.persist(record);
    await this.em.flush();
    const carId = record.id;
    return carId;
  }

and returns carId. A separate repository persists Driver entity and tries to create 1:1 relationship at that time:

  async persistDriverForCar(body: { driverName: string; carId: string }) {
   const record = new DriverEntity();
   record.name = body.driverName;
   const car = this.em.getReference(CarEntity, body.carId);
   record.car = car;
   this.em.persist(car);
   await this.em.flush();
 }

the getReference method successfully obtains reference to previously-persisted Car entity in both 5.7.7 and 5.7.8. The generated SQL, however, refers to the Car entity successfully in 5.7.7 but doesn't in 5.7.8.

Expected behavior

Persisting new entity with reference to another entity obtained via getReference method should allow for persistence of 1:1 relationship.

Additional context

Thank you so much for your amazing work on MikroORM!

Repro code is a bit of an example - I don't normally import domain modules to another modules in NestJS and use EventEmitter but I've done that here to simplify.

I do wonder if perhaps I was doing something against Mikro-ORM that I shouldn't have been but wanted to let you know in case this is an unintentional breaking change.

Versions

See package.json in repro.

Dependency Version
node 20.1.0
typescript 5.1.3
mikro-orm 5.7.8 (works ?as intended on 5.7.7)
your-driver postgresql
@B4nan
Copy link
Member

B4nan commented Jul 4, 2023

Could you please turn this into a script instead of a nestjs app? Doesn't have to be a test case per se, but something I can just run and see it fail, instead of starting a web server and doing requests to it.

@merkator95
Copy link
Author

Of course, thanks!
Scripted repro

Repro script works fine with SQLite but issue is reproduced with MikroORM 5.7.12 and postgres driver on pg15 (compose file in repo).

test('persisting 1:1 relation issue', async () => {
  const em = orm.em.fork();

  let car = em.create(CarEntity, { brand: 'skoda' });
  await em.persist(car).flush();

  const carReference = em.getReference(CarEntity, car.id);

  let driver = em.create(DriverEntity, { name: 'John Doe', car: carReference }); 
  const driverId = driver.id;
  await em.persist(driver).flush();

  const managedCar = car;
  expect(managedCar.driver).toBeDefined();
  expect(managedCar.driver.id).toStrictEqual(driverId); // Driver is defined and has an id in managed entity

  const foundCar = await em.findOneOrFail(CarEntity, { id: car.id });
  expect (foundCar).toBeDefined();
  expect (foundCar.driver).not.toBeNull(); // Fails, driver is null in found entity
});

What I got so far is:

  1. Seems to be introduced with #7127ff6, #b5ab66bc works fine
  2. If entity type doesn't have a version field, it works fine
  @Property({ version: true })
  version!: number; /// If this is commented out, issue is not reproduced

I've been looking into the internals of MikroORM and would love to try and offer a fix but I'm failing thus far - do let me know if there is anything else I can do.

@B4nan B4nan added the bug Something isn't working label Jul 14, 2023
@B4nan B4nan closed this as completed in 474eb73 Jul 14, 2023
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

2 participants