typeorm: how do I correctly save a lazy related entity with {nullable: false}?

245 Views Asked by At

Stack:

  • Typeorm: 0.3.15
  • Node
  • Express: ^4.18.2
  • Typescript
  • MySQL

I have a User entity in my application that has a reference property to its parent Organization. I've mapped the organization property as a Promise to enforce lazy loading and its reference column, oid, is also mapped using { nullable: false } to ensure that the organization is set on a user before that user can be saved to the database:

User:


    @Entity('users')
    export class User extends IdentifiableEntity implements OrganizationalEntity {
      
      ...
    
      @ManyToOne(() => Organization, org => org.users, {
        nullable: false,
      })
      @JoinColumn({name: 'oid'})
      org: Promise<Organization>;
    
      ...
    
    }

Organization:


    @Entity('organizations')
    export class Organization extends IdentifiableEntity {
      
      ...
    
      @OneToMany(() => User, user => user.org)
      users: Promise<User[]>;
    
      ...
    
    }

When I set the organization prior to calling save on the User repository, I'm setting it using: user.org = Promise.resolve(new Organization(oid)) as indicated in the typeorm documentation for lazy references and shown in the documentation on how to save a lazy relation.

Unfortunately, the save fails with the following MySQL database error:


    QueryFailedError: Field 'oid' doesn't have a default value
        at Query.onResult (/Users/me/workspace/api/src/driver/mysql/MysqlQueryRunner.ts:222:33)
        at Query.execute (/Users/me/workspace/api/node_modules/mysql2/lib/commands/command.js:36:14)
        at PoolConnection.handlePacket (/Users/me/workspace/api/node_modules/mysql2/lib/connection.js:489:32)
        at PacketParser.onPacket (/Users/me/workspace/api/node_modules/mysql2/lib/connection.js:94:12)
        at PacketParser.executeStart (/Users/me/workspace/api/node_modules/mysql2/lib/packet_parser.js:75:16)
        at Socket.<anonymous> (/Users/me/workspace/api/node_modules/mysql2/lib/connection.js:101:25)
        at Socket.emit (node:events:513:28)
        at Socket.emit (node:domain:489:12)
        at addChunk (node:internal/streams/readable:324:12)
        at readableAddChunk (node:internal/streams/readable:297:9) {
      query: 'INSERT INTO `users`(`id`, `createdAt`, `updatedAt`, `deletedAt`, `firstName`, `lastName`, `department`, `position`, `startDate`, `emails`, `oid`) VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT, ?, ?, ?, ?, ?, ?, DEFAULT)',
      parameters: [
        'New',
        'User',
        'Marketing',
        'Head of Marketing',
        2023-05-04T07:42:18.000Z,
        '["[email protected]"]'
      ],
      driverError: Error: Field 'oid' doesn't have a default value

If I remove the { nullable: false } constraint from the org property on the User entity, the user is saved in the database but the oid property is never set, leaving a null value for oid in the User row.

Has anyone else ran into this and, if so, what's the best way to resolve this error?

I have found a few posts in other online forums that have suggested defining the relation as:

User:


    @Entity('users')
    export class User extends IdentifiableEntity implements OrganizationalEntity {
      
      ...
    
      @ManyToOne(() => Organization, org => org.users, {
        nullable: false,
      })
      @JoinColumn({name: 'oid'})
      org: Promise<Organization> | Organization;
    
      ...
    
    }

and setting the property using user.org = new Organization(oid), without the Promise. This does work, however, I didn't find anything on the Typeorm website that indicates this is a correct solution. I'm also unsure how having both Promise<Organization> and Organization as types for the User.org property will affect lazy loading of that entity. e.g. will it still be lazy because one of the options is a Promise?

1

There are 1 best solutions below

0
Noobogami On

I have similar problem as this answer I changed object type in entity to Promise<T> and tried to fill them with Promise.resolve() but it didn't fill column and because that field couldn't be nullable I got error. I rolled back that field from Promise<T> to T and as I tested everything seems ok, I didn't test if query will get lazy object or not so don't know if didn't using Promise bypassed lazy functionality (don't know if it's related to that or not)

ps. according to this reddit comment that led to this release todo by typeorm creator (pleerock) it seems lazy and eager are not ideal and he tries to deprecate them.

here's more description by pleerock

EDIT: i found this question today and it seems related. I didn't test it yet but it seems we need to use cascade for this to work.