在 typeORM 中的 @ManyToMany 中保存关系会覆盖先前的条目

Posted

技术标签:

【中文标题】在 typeORM 中的 @ManyToMany 中保存关系会覆盖先前的条目【英文标题】:Save relation in @ManyToMany in typeORM overwrites previous entry 【发布时间】:2021-07-19 06:23:37 【问题描述】:

我用typeorm/mysqlnodejs/express做了一个追随者系统。

保存逻辑:

router.post("/subscriptions", async (req, res) => 
  const currentUserId = Number(req.headers.userid);
  const userToFollowId = req.body.userToFollowId;

  const userRepository = getRepository(User);

  try 
    let user;
    let userToFollow;

    user = await userRepository.findOneOrFail(currentUserId);
    userToFollow = await userRepository.findOneOrFail(userToFollowId);

    if (user && userToFollow) 
      user.followers = [...(user?.followers || []), userToFollow];
      await userRepository.save(user);
      res.status(201).send("Followed");
    
   catch (e) 
    console.log(e);
  
);

@User具有自引用关系的实体:

@Entity()
export class User 
  @PrimaryGeneratedColumn()
  id!: number;

  @Column( nullable: true )
  name!: string;

  @Column()
  email!: string;

  @Column()
  password!: string;

  @OneToMany(() => Post, (post) => post.user)
  posts!: Post[];

  @OneToMany(() => Comment, (comment) => comment.user)
  comments!: Comment[];

  @ManyToMany(() => User, (user) => user.following,  cascade: true )
  @JoinTable()
  followers!: User[];

  @ManyToMany(() => User, (user) => user.followers)
  following!: User[];

  @Column(
    default: () => "NOW()",
  )
  createdAt!: Date;

  @Column(
    default: () => "NOW()",
  )
  @UpdateDateColumn()
  updatedAt!: Date;

假设curentUserId 为5,userToFollow ID 为6。它将成功保存到数据库中。当我尝试通过使用相同的 currentUserIdof 5 和 userToFollow 一些其他 ID(例如 7)来保存另一个条目时会出现问题,在这种情况下是数据库中的前一个条目: 用户 5 关注 ID 为 6 的用户将是 overwritten 到: 用户5关注ID为7的用户

这是我第一次保存时的查询:

query: SELECT `User`.`id` AS `User_id`, `User`.`name` AS `User_name`, `User`.`email` AS `User_email`, `User`.`password` AS `User_password`, `User`.`createdAt` AS `User_createdAt`, `User`.`updatedAt` AS `User_updatedAt` FROM `user` `User` WHERE `User`.`id` IN (?) -- PARAMETERS: [5]
query: SELECT `User`.`id` AS `User_id`, `User`.`name` AS `User_name`, `User`.`email` AS `User_email`, `User`.`password` AS `User_password`, `User`.`createdAt` AS `User_createdAt`, `User`.`updatedAt` AS `User_updatedAt` FROM `user` `User` WHERE `User`.`id` IN (?) -- PARAMETERS: ["6"]
query: SELECT `User`.`id` AS `User_id`, `User`.`name` AS `User_name`, `User`.`email` AS `User_email`, `User`.`password` AS `User_password`, `User`.`createdAt` AS `User_createdAt`, `User`.`updatedAt` AS `User_updatedAt` FROM `user` `User` WHERE `User`.`id` IN (?, ?) -- PARAMETERS: [5,6]
query: SELECT `User_followers_rid`.`userId_1` AS `userId_1`, `User_followers_rid`.`userId_2` AS `userId_2` FROM `user` `user` INNER JOIN `user_followers_user` `User_followers_rid` ON (`User_followers_rid`.`userId_1` = ? AND `User_followers_rid`.`userId_2` = `user`.`id`) OR (`User_followers_rid`.`userId_1` = ? AND `User_followers_rid`.`userId_2` = `user`.`id`)  ORDER BY `User_followers_rid`.`userId_2` ASC, `User_followers_rid`.`userId_1` ASC -- PARAMETERS: [5,6]
query: START TRANSACTION
query: INSERT INTO `user_followers_user`(`userId_1`, `userId_2`) VALUES (?, ?) -- PARAMETERS: [5,6]
query: COMMIT

这是我认为出现问题的第二个问题(请注意DELETE 部分):

query: START TRANSACTION
query: INSERT INTO `user_followers_user`(`userId_1`, `userId_2`) VALUES (?, ?) -- PARAMETERS: [5,6]
query: COMMIT
query: SELECT `User`.`id` AS `User_id`, `User`.`name` AS `User_name`, `User`.`email` AS `User_email`, `User`.`password` AS `User_password`, `User`.`createdAt` AS `User_createdAt`, `User`.`updatedAt` AS `User_updatedAt` FROM `user` `User` WHERE `User`.`id` IN (?) -- PARAMETERS: [5]
query: SELECT `User`.`id` AS `User_id`, `User`.`name` AS `User_name`, `User`.`email` AS `User_email`, `User`.`password` AS `User_password`, `User`.`createdAt` AS `User_createdAt`, `User`.`updatedAt` AS `User_updatedAt` FROM `user` `User` WHERE `User`.`id` IN (?) -- PARAMETERS: ["7"]
query: SELECT `User`.`id` AS `User_id`, `User`.`name` AS `User_name`, `User`.`email` AS `User_email`, `User`.`password` AS `User_password`, `User`.`createdAt` AS `User_createdAt`, `User`.`updatedAt` AS `User_updatedAt` FROM `user` `User` WHERE `User`.`id` IN (?, ?) -- PARAMETERS: [5,7]
query: SELECT `User_followers_rid`.`userId_1` AS `userId_1`, `User_followers_rid`.`userId_2` AS `userId_2` FROM `user` `user` INNER JOIN `user_followers_user` `User_followers_rid` ON (`User_followers_rid`.`userId_1` = ? AND `User_followers_rid`.`userId_2` = `user`.`id`) OR (`User_followers_rid`.`userId_1` = ? AND `User_followers_rid`.`userId_2` = `user`.`id`)  ORDER BY `User_followers_rid`.`userId_2` ASC, `User_followers_rid`.`userId_1` ASC -- PARAMETERS: [5,7]
query: START TRANSACTION
query: INSERT INTO `user_followers_user`(`userId_1`, `userId_2`) VALUES (?, ?) -- PARAMETERS: [5,7]
query: DELETE FROM `user_followers_user` WHERE `userId_1` = ? AND `userId_2` = ? -- PARAMETERS: [5,6]
query: COMMIT

【问题讨论】:

【参考方案1】:

你有这个问题的原因是当你查询用户时,你没有添加 followers 与它的关系。写一个console.log(user),你会看到user对象没有followers属性。这就是为什么每次调用 api 时,它都会用新的替换旧的。

您必须在查询中添加followers 关系。这将以user.followersUser 对象的形式返回所有现有的关注者列表。您可以使用的另一件事 user.followers.push(userToFollow); 而不是 user.followers = [...(user?.followers || []), userToFollow]; 在您的代码中。使用下面的代码,

try 
            let user = await userRepository.findOneOrFail(currentUserId,  relations: ["followers"] );
            let userToFollow = await userRepository.findOneOrFail(userToFollowId);

            if (user && userToFollow) 
                user.followers.push(userToFollow);
                await userRepository.save(user);
                return res.status(201).send("Followed");
             else 
                throw new Error("User not found!")
            
         catch (e) 
            console.log(e);
        

【讨论】:

非常感谢!它就像一个魅力 :) 我想知道为什么当我尝试在 follower 属性上使用 .push 方法时会出现未定义的错误,但现在这也很有意义!

以上是关于在 typeORM 中的 @ManyToMany 中保存关系会覆盖先前的条目的主要内容,如果未能解决你的问题,请参考以下文章

NestJS + TypeORM 中的 JoinTable 问题

TypeORM 中的 Postgres 枚举

查询中的 TypeORM、反引号与冒号

如何在typeorm中加入树结构中的每个子实体

使用 typeorm 在 postgres 中搜索数组中的项目

TypeORM/NestJS 中的分页