【问题标题】:How to make Inner Join to work on TypeORM?如何让 Inner Join 在 TypeORM 上工作?
【发布时间】:2021-04-08 01:44:08
【问题描述】:

我正在尝试在 TypeORM 上构建一个简单的查询,但我没有使用 INNER JOIN 获取全部数据。我做错了什么?

SQL 查询运行完美,但 typeorm 只返回“观察者”表的数据。

SQL 查询

SELECT *
FROM watcher w
INNER JOIN user
ON w.userId = user.id;

TypeORM

async getSystemWideWatchers(): Promise<any[]> {
    const query = this.createQueryBuilder('watcher');
    const result = await query.innerJoin('user', 'u', 'watcher.userId = u.id').getMany();
    console.log(result)
    return result;
}

【问题讨论】:

    标签: nestjs typeorm


    【解决方案1】:

    TypeORM 有一个名为innerJoinAndSelect 的方法。你使用普通的innerJoin。这就是为什么没有从中选择用户表的原因。

    将该部分更改为innerJoinAndSelect 后,将从中选择监视表。但是,getManygetOne 返回具有您的 Entity 类型的对象。因此,如果您的 Entity 类型没有 User 和 Watcher 表之间的关系,则所选列将不会包含在返回的对象中。

    在我向您展示如何添加这些关系之前,我想提一下您可以选择使用getRawMany 函数来获取所有选定的列,但我不建议使用它,因为关系更整洁(没有原始列名,您将获得与关系相对应的实体数组),在您的情况下,没有理由不使用关系。

    现在,我理解您的数据库设计的方式是,您有用户,而用户有观察者。一个 Watcher 只关注一个 User,可能有多个 Watcher 关注同一个用户。

    在这种情况下,用户与观察者的关系称为“一对多”。

    Watcher 与 User 的关系称为“多对一”。

    您需要在您的实体中指定此信息。这是一个例子。注意 OneToMany 装饰器。

    @Entity()
    export class User {
        @PrimaryColumn()
        id: number;
    
        @Column()
        userName: string;
    
        @OneToMany(type => Watcher, watcher => watcher.user)
        watchers: Watcher[];
    }
    

    这里是对应的Watcher Entity:

    @Entity()
    export class Watcher {
        @PrimaryColumn()
        id: number;
    
        @Column()
        watcherName: string;
    
        // we can omit this (and the join condition), if userId is a foreign key
        @Column()
        userId: number;
    
        @ManyToOne(type => User, user => user.watchers)
        user: User;
    }
    

    建立这些关系后,您可以选择一个用户及其所有观察者,反之亦然(选择一个观察者及其观察的用户)。

    这两种方法都是这样的:

    // Select a user and all their watchers
    const query = createQueryBuilder('user', 'u')
        .innerJoinAndSelect('u.watchers', 'w'); // 'w.userId = u.id' may be omitted
    const result = await query.getMany();
    

    (如果您想包含没有观察者的用户,您可能知道使用 leftJoin 而不是 innerJoin。)

    这可能是您最初尝试做的事情:

    // Select a watcher and the user they watch
    const query = createQueryBuilder('watcher', 'w')
        .innerJoinAndSelect('w.user', 'u'); // 'w.userId = u.id' may be omitted
    const result = await query.getMany();
    

    【讨论】:

    • 你可能没有必要的关系。我更新了我的答案。你能再检查一下吗?
    • 兄弟你明白了!这些实体正是我所拥有的。问题是我尝试使用user.idwatcher.userId 而不是watcher.user 加入。非常非常感谢你!新年快乐!
    • 啊 - 你有关系,但没有使用它......我很高兴它已经修复,也祝你新年快乐!
    【解决方案2】:

    以防万一有人遇到其他情况。添加到 Cem 的回答中。 如果数据库中没有定义关系,那么您可以执行以下操作:

    const query = createQueryBuilder('user', 'u')    
    .innerJoinAndMapMany(
        'u.ObjectNameToMapDataOn',
        EntityName,// or 'tableName'
        'IAmAlias',
        'u.columnName= IAmAlias.columnName'
      )
    

    类似地,innerJoinAndMapOne 也可以使用。如果您希望有多个记录与连接表或单个记录,则取决于您的情况。

    【讨论】:

    • 谢谢你真的很有帮助
    【解决方案3】:

    如果两个表之间没有关系,则必须使用getRow()

    getOne()getMany() 将返回具有存储库类型的对象,因此删除连接表的数据

    这个例子对我有用:

    this.userRepository
                .createQueryBuilder('user')
                .select(['user.username as username', 'o.orderTime as orderTime'])
                .where('user.id = :id', {id})
                .andWhere('o.callerId = :id', {id})
                .innerJoin(OrderEntity, 'o')
                .getRawMany()
    

    【讨论】:

      猜你喜欢
      • 2021-06-30
      • 1970-01-01
      • 2021-07-31
      • 2015-10-27
      • 2019-08-12
      • 2011-11-27
      • 2011-01-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多