TypeORM:更新项目并返回它

76
据我所知,更新后最好返回一个项目。TypeORM的updateById返回void,而不是更新后的项目。
我的问题是:是否可以在单行中更新并返回修改后的项目?
我已尝试过的内容:
await this.taskRepository.updateById(id, { state, dueDate });
return this.taskRepository.findOne({ id });

我所寻找的是:

return this.taskRepository.updateById(id, { state, dueDate }); // returns updated task
5个回答

108

我刚刚发现我可以使用.save方法来完成这个操作:

return this.taskRepository.save({
    id: task.id,
    state,
    dueDate
});
根据文档save部分),也支持局部更新:

由于跳过所有未定义的属性,因此还支持部分更新。


@Dugi 这只是一个实体。是的,在问题中提到了它。甚至存储库也是这样命名的。 :) - sandrooco
5
我在谈论 ..task 变量的展开,这个变量突然出现并不在原问题中;而在原问题中你使用的是 id - aborted
23
请注意,.save 会在实体不存在时创建一个新实体。这可能不总是希望发生的情况。因此,.update 将是一个更明智的选择,然后仍要执行 .findOne(id: task.id) 返回整个对象。 - DarkLite1
我可能不正确,但是根据我的测试,当你使用.save来更新实体时,如果你使用{...oldEntity, ...updates},这个方法并没有返回实体本身,而只是一个普通的对象。这可能会对class-serializer造成问题。 - Baterka
2
@sandrooco 我改口道歉了。我进行了进一步的测试,它确实返回了完整的实体。我很抱歉,并已经点赞了这个答案。此外,我提出了一些改进措施以便更全面。因为我们确实希望更新并返回实体,而 #save 的缺陷是无论是否有意,都会创建新的实体。 - Calvintwr
显示剩余4条评论

41

在sandrooco的回答上进行扩展,这是我所做的:

const property = await this.propertyRepository.findOne({
  where: { id }
});

return this.propertyRepository.save({
  ...property, // existing fields
  ...updatePropertyDto // updated fields
});

9
这段代码仍需要对数据库进行两次调用。有没有办法只进行一次调用呢?例如,在Postgres中,您可以运行以下代码:UPDATE .. SET .. RETURNING *这样它就会更新数据并返回已更新的行。 - Chidiebere
@Chidiebere 我也遇到了同样的问题。你找到了一种方法可以在不重复选择语句的情况下保存吗? - ctilley79
我没有找到解决这个问题的方法(我只是在那里进行了两次调用),但我已经决定在任何出现这种优化问题的地方编写原始查询,以便原始查询可以帮助我。 - Chidiebere
这有点没用,你甚至不需要现有的字段(当然除了 id)。 - sandrooco

32

关键是返回response.raw[0]以获取类型。


虽然我希望 await Table.update({}, {}) 返回 Table,但实际上它并不会。我发现使用 QueryBuilder 更容易控制,但是如果你不喜欢 QueryBuilder 或者不需要它,你可以像这样做:

const post = Post.update({id}, {...input}).then(response => response.raw[0]);
// or
const post = (await Post.update({id}, {...input})).raw[0];

return post; // returns post of type Post

如果您确实想使用QueryBuilder,我建议采用以下方法。 其他人已经提到了RepositoryTable.save()的用法,但这些方法并没有真正返回原始的type,所以对我来说这种方法不可行。 Table.update({}, {})的示例:
@Mutation(() => PostResponse, { nullable: true })
@UseMiddleware(isAuthorized)
async updatePost(
  @Arg("id", () => Int) id: number,
  @Arg("input") input: PostInput,
  @Ctx() { req }: Context
): Promise<PostResponse | null> {
  // ...
  const post = await Post.update({id}, {...input}).then(response => response.raw[0]);
  return { post };
}

QueryBuilder的一个示例:

@Mutation(() => PostResponse, { nullable: true })
@UseMiddleware(isAuthorized)
async updatePost(
  @Arg("id", () => Int) id: number,
  @Arg("input") input: PostInput,
  @Ctx() { req }: Context
): Promise<PostResponse | null> {
  //...
  Post.createQueryBuilder()
    .update(Post)
    .set({ ...input })
    .where('id = :id and "creatorId" = :creatorId', {
      id,
      creatorId: userId,
    })
    .returning("*")
    .execute()
    .then((response) => {
      return response.raw[0];
    });

  return { post };
}

辅助函数(如果您不想一直编写response.raw[0]

const typeReturn = async <T>(mutation: Promise<UpdateResult | DeleteResult | InsertResult>): Promise<T> => {
  return (await mutation).raw[0];
};

使用方法:

const update = await typeReturn<Post>(Post.update(...));
const insert = await typeReturn<Attachment>(Attachment.insert(...));
const del    = await typeReturn<User>(User.delete(...));

注意:我这里正在使用 TypeORM 和 Type-GraphQL。
在 MySQL 上,.returning("*") 不起作用,请参见下面的评论。

1
这似乎是一个比使用查找/保存或更新/查找更好的选项。有趣的是,这没有得到赞同。 - vijayakumarpsg587
1
@Joey587,我更新了答案,包括使用Table.update({}, {})方法的不同方法。 - Joel
感谢分享,@Joel这个问题应该明确说明它是针对 Microsoft SQL Server 和/或 PostgreSQL 的,因为 MySQL 不支持 .returning("*")。这使得那些使用 MySQL 的人无法使用这个答案。 - Rafael Leite
@RafaelLeite 很好的发现,我自己还没有验证过,但我可以添加一条相关的注释。 - Joel
1
有人可以把这个标记为正确答案吗? - enanone

1
一种方法是执行更新,然后根据您指定的条件进行查找。

2
这会向数据库发出2个查询,并且不是原子性的。 - Xetera
@Xetera 非常正确。 如果您正在使用PostgreSQL,则可以编写执行更新并返回它的原始查询(请参见此处)。例如。UPDATE SET c=value RETURNING * 如果您没有使用Postgres,则最好在事务中执行更新和查找。您认为呢? - Chidiebere
1
这正是我不想做的事情... - sandrooco

1

让我们这样做来验证.save方法是否存在:

async findOne(id: string): Promise<Manufacturer> {
    const found = await this.repository.findOneBy({ id });

    if (!found) {
        throw new NotFoundException(`Could not find ${this.Entity.name} with id: ${id}`);
    }

    return found;
}

async update(id: string, updateEmployeeDto: UpdateManufacturerDto): Promise<Manufacturer> {
    await this.findOne(id);

    return this.repository.save({ id, ...updateEmployeeDto });
}


1
  1. 问题是“是否可以在单行中更新并返回修改后的项目?” --> 单行
  2. 对于这个问题,有一个名为findOneOrFail的函数。
- sandrooco
哦,感谢澄清,我不知道那个。我是TypeORM的新手。 - Adiat Hasan
.raw[0]是一个空数组,不起作用。 - Mehdi Faraji

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接