【问题标题】:TypeORM : relation with where clause defined in entityTypeORM:与实体中定义的 where 子句的关系
【发布时间】:2021-08-13 10:59:14
【问题描述】:

我想创建一个 SalesOrder 实体,它与同一实体有 2 个 OneToMany 关系,但条件不同。

最终目标是检索这样的地址:

const salesOrder = await SalesOrder.findOne(1, {
    relations: ['receiverAddress', 'senderAddress']
});
console.log(salesOrder.receiverAddress)
console.log(salesOrder.senderAddress)

我不知道如何在 SalesOrder 实体中按类型过滤地址关系

我想做这样的事情:

// SalesOrder.ts
@Entity()
export class SalesOrder extends BaseEntity {
  @Column()
  @PrimaryGeneratedColumn()
  id: number

  @OneToMany(SalesOrderAddress, salesOrderAddress => salesOrderAddress, {
    where: {
      type: 'receiver' // join condition salesOrderAddress.type = 'receiver'
    }
  })
  receiverAddress: SalesOrderAddress

  @OneToMany(SalesOrderAddress, salesOrderAddress => salesOrderAddress, {
    where: {
      type: 'sender' // join condition salesOrderAddress.type = 'sender'
    }
  })
  senderAddress: SalesOrderAddress
}

问题: where 子句不在 typeorm 装饰器中处理。

是否有可能得到类似的东西(不使用查询生成器)?

我知道我可以使用 2 个 OneToOne 关系,但这意味着 SalesOrder 数据库包含两个外键:receiverAddressId 和 senderAddressId。我更喜欢使用 OneToMany 关系,因为它允许在 SalesOrderAddress 上只有一个外键(例如 salesOrderId)。

【问题讨论】:

    标签: node.js typescript typeorm


    【解决方案1】:

    在您的情况下,我会考虑创建自定义方法来获取所需的信息。检查该示例:

    import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToMany, ManyToOne } from 'typeorm';
    
    @Entity({ name: 'Address' })
    export class Address extends BaseEntity {
      @PrimaryGeneratedColumn({ type: 'int' })
      id: number;
    
      @Column({ type: 'varchar', length: 20 })
      type: 'receiver' | 'sender';
    
      @Column({ type: 'nvarchar', length: 1024 })
      value: string;
    
      // Put the prop that connects this entity to SalesOrder
      @ManyToOne(SalesOrder, ref => ref.addresses)
      salesOrder: SalesOrder;
    }
    
    @Entity({ name: 'SalesOrder' })
    export class SalesOrder extends BaseEntity {
      @PrimaryGeneratedColumn({ type: 'int' })
      id: number;
    
      // Put the prop that connects this entity to Address
      @OneToMany(Address, ref => ref.salesOrder)
      addresses: Address[];
    
      // Create a generic method to filter
      private static getTypeAddresses(id: number, type: 'receiver' | 'sender'): Promise<Address[]> {
        if (typeof id !== 'number') {
          throw new Error('The entity\'s id must be a number');
        }
    
        return Address
          .createQueryBuilder('Address')
          .select([ 'Address' ])
          .innerJoin(
            'Address.salesOrder',
            'SalesOrder',
            'SalesOrder.id = :id',
            { id: id }
          )
          .where(
            'type = :type',
            { type }
           )
          .getMany();
      }
    
      // Later, create your methods to obtain easifully your address
      static getReceiverAddresses(id: number): Promise<Address[]> {
        return SalesOrder.getTypeAddresses(id, 'receiver');
      }
    
      static getSenderAddresses(id: number): Promise<Address[]> {
        return SalesOrder.getTypeAddresses(id, 'sender');
      }
    
      getReceiverAddresses(): Promise<Address[]> {
        return SalesOrder.getTypeAddresses(this.id, 'receiver');
      }
    
      getSenderAddresses(): Promise<Address[]> {
        return SalesOrder.getTypeAddresses(this.id, 'sender');
      }
    }
    

    使用示例:

    const salesOrder = await SalesOrder.findOne({ id: 1 });
    console.log(await salesOrder.getReceiverAddresses());
    console.log(await salesOrder.getSenderAddresses());
    

    如果您不想为实体类添加方法,请考虑创建一个控制器类或其他更方便的过程。例如:

    import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToMany, ManyToOne } from 'typeorm';
    
    @Entity({ name: 'Address' })
    export class Address extends BaseEntity {
      @PrimaryGeneratedColumn({ type: 'int' })
      id: number;
    
      @Column({ type: 'varchar', length: 20 })
      type: 'receiver' | 'sender';
    
      @Column({ type: 'nvarchar', length: 1024 })
      value: string;
    
      // Put the prop that connects this entity to SalesOrder
      @ManyToOne(SalesOrder, ref => ref.addresses)
      salesOrder: SalesOrder;
    }
    
    @Entity({ name: 'SalesOrder' })
    export class SalesOrder extends BaseEntity {
      @PrimaryGeneratedColumn({ type: 'int' })
      id: number;
    
      // Put the prop that connects this entity to Address
      @OneToMany(Address, ref => ref.salesOrder)
      addresses: Address[];
    }
    
    export class SalesOrderCtrl extends SalesOrder {
      private static getTypeAddresses(id: number, type: 'receiver' | 'sender'): Promise<Address[]> {
        if (typeof id !== 'number') {
          throw new Error('The entity\'s id must be a number');
        }
    
        return Address
          .createQueryBuilder('Address')
          .select([ 'Address' ])
          .innerJoin(
            'Address.salesOrder',
            'SalesOrder',
            'SalesOrder.id = :id',
            { id: id }
          )
          .where(
            'type = :type',
            { type }
           )
          .getMany();
      }
    
      static getReceiverAddresses(id: number): Promise<Address[]> {
        return SalesOrder.getTypeAddresses(id, 'receiver');
      }
    
      static getSenderAddresses(id: number): Promise<Address[]> {
        return SalesOrder.getTypeAddresses(id, 'sender');
      }
    
      getReceiverAddresses(): Promise<Address[]> {
        return SalesOrder.getTypeAddresses(this.id, 'receiver');
      }
    
      getSenderAddresses(): Promise<Address[]> {
        return SalesOrder.getTypeAddresses(this.id, 'sender');
      }
    }
    

    使用示例:

    const salesOrder = await SalesOrderCtrl.findOne({ id: 1 });
    console.log(await salesOrder.getReceiverAddresses());
    console.log(await salesOrder.getSenderAddresses());
    

    【讨论】:

    • 已接受答案。但是对于一个干净(且不那么麻烦)的解决方案,我不得不迁移到 Prisma ORM,它具有我需要的所有功能。迁移可以逐步完成,因为您可以同时使用 TypeORM 和 Prisma ORM。
    猜你喜欢
    • 2021-12-27
    • 1970-01-01
    • 1970-01-01
    • 2011-11-01
    • 2018-06-16
    • 1970-01-01
    • 2015-07-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多