【问题标题】:TypeORM: edit a many-to-many relations with custom propertiesTypeORM:使用自定义属性编辑多对多关系
【发布时间】:2021-02-08 17:54:11
【问题描述】:

如何编辑具有自定义属性的多对多关系的实体? 对于这种类型的关系,Typeorm 文档示例非常有限: https://typeorm.io/#/many-to-many-relations/many-to-many-relations-with-custom-properties

我有 3 个表:用户、角色、用户角色

用户.ts

@Entity('users')
class User {
  @PrimaryGeneratedColumn('increment')
  id: number;

  @Column()
  name: string;

  @Column()
  email: string;
  
  @OneToMany(() => UserRole, userRole => userRole.user, {
    cascade: true,
    onDelete: 'CASCADE',
    onUpdate: 'CASCADE',
  })
  @JoinColumn({ referencedColumnName: 'user_id' })
  userRoles!: UserRole[];
}

export default User;

角色.ts

@Entity('roles')
class Role {
  @PrimaryGeneratedColumn('increment')
  id: number;

  @Column()
  role: string;

  @OneToMany(() => UserRole, userRole => userRole.role, {
    cascade: true,
    onDelete: 'CASCADE',
    onUpdate: 'CASCADE',
  })
  @JoinColumn({ referencedColumnName: 'role_id' })
  userRoles!: UserRole[];
}

export default Role;

UserRole.ts

@Entity('user_roles')
class UserRole {
 @PrimaryGeneratedColumn('increment')
 id: number;

 @Column()
 user_id!: number;

 @Column()
 role_id!: number;
 
 @CreateDateColumn()
 created_at: Date;

 @UpdateDateColumn()
 updated_at: Date;

 @ManyToOne(() => User, user => user.userRoles)
 @JoinColumn({ name: 'user_id' })
 user!: User;

 @ManyToOne(() => Role, role => role.userRoles)
 @JoinColumn({ name: 'role_id' })
 role!: Role;
}

export default UserRole;

创建具有相关 userRoles 的用户正在工作:

const user = {
  name: 'Test user',
  email: 'testuser@test.com',
  userRoles: [
    { role_id: 1 },
    { role_id: 2 }, 
    { role_id: 3 },
    { role_id: 4 }
  ],
};

await this.usersRepository.save(user);

更新它不起作用,我想用新的替换所有相关的 user_roles,typeorm 多对多关系中是否有任何保存策略选项?我在其他形式中看到过,例如关系定义中的“替换”或“附加”选项 这就是我尝试更新的方式:

// load user
const user = await this.usersRepository.findById(user_id);

/*
returns:

User {
 id: 2,
 name: 'Test user',
 email: 'testuser@test.com',
 userRoles: [
   UserRole {
     id: 376,
     user_id: 2,
     role_id: 1,
     created_at: 2020-09-18T17:26:52.000Z,
     updated_at: 2020-09-18T17:26:52.000Z
   },
   UserRole {
     id: 377,
     user_id: 2,
     role_id: 2,
     created_at: 2020-09-18T17:26:52.000Z,
     updated_at: 2020-09-18T17:26:52.000Z
   },
   UserRole {
     id: 378,
     user_id: 2,
     role_id: 3,
     created_at: 2020-09-18T17:26:53.000Z,
     updated_at: 2020-09-18T17:26:53.000Z
   },
   UserRole {
     id: 379,
     user_id: 2,
     role_id: 4,
     created_at: 2020-09-18T17:26:53.000Z,
     updated_at: 2020-09-18T17:26:53.000Z
   }
 ]
}
*/

// edit user
const newRolesIds = [4, 5, 6, 7];

user.name = 'updated name';
user.email = 'updatedemail@test.com';


// isn`t it suposed to replace old user_roles and create this new ones?
user.userRoles = newRolesIds.map(roleId => {
 // keep the roles that are already saved in database and are in the newRolesIds 
     const existingRole = user.userRoles.find(userRole => userRole.role_id === roleId);

     if (existingRole) {
       return existingRole;
     }

     // add new roles
     const userRole = new UserRole();
     userRole.user_id = user.id;
     userRole.role_id = roleId;
     return userRole;
});

/*
user after edit:
User {
 id: 2,
 name: 'updated name',
 email: 'updatedemail@test.com',
 userRoles: [
   UserRole { user_id: 2, role_id: 4 },
   UserRole { user_id: 2, role_id: 5 },
   UserRole { user_id: 2, role_id: 6 },
 ]
}
*/

this.ormRepository.save(user);

保存失败并出现以下 sql 错误: 它是一个奇怪的更新 sql,无法识别 user_id 和 role_id 甚至不存在于 sql 中,我是否忘记在关系中定义一些东西?

QueryFailedError: Cannot add or update a child row: a foreign key constraint fails (`user_roles`, CONSTRAINT `UserRoleUserIdFK` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE)
  code: 'ER_NO_REFERENCED_ROW_2',
  errno: 1452,
  sqlState: '23000',
  sqlMessage: 'Cannot add or update a child row: a foreign key constraint fails (`user_roles`, CONSTRAINT `UserRoleUserIdFK` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE)',
  query: 'UPDATE `user_roles` SET `user_id` = ?, `updated_at` = CURRENT_TIMESTAMP WHERE `id` = ?',
  parameters: [ undefined, 376 ]

sql 日志显示插入新角色 5、6、7,然后尝试更新应该删除的角色 1、2、3,引发 sql 错误

根据文档,只需从数组中删除即可删除,还是我应该手动删除? https://typeorm.io/#/many-to-many-relations/deleting-many-to-many-relations

query: SELECT `user_roles`.`id` AS `id`, `user_roles`.`user_id` AS `user_id` FROM `user_roles` `user_roles` WHERE ((`user_roles`.`user_id` = ?)) -- PARAMETERS: [2]
query: START TRANSACTION
query: INSERT INTO `user_roles`(`id`, `user_id`, `role_id`, `created_at`) VALUES (DEFAULT, ?, ?, DEFAULT) -- PARAMETERS: [2,5]
query: SELECT `UserRole`.`id` AS `UserRole_id`, `UserRole`.`created_at` AS `UserRole_created_at` FROM `user_roles` `UserRole` WHERE `UserRole`.`id` = ? -- PARAMETERS: [7]
query: INSERT INTO `user_roles`(`id`, `user_id`, `role_id`, `created_at`) VALUES (DEFAULT, ?, ?, DEFAULT) -- PARAMETERS: [2,6]
query: SELECT `UserRole`.`id` AS `UserRole_id`, `UserRole`.`created_at` AS `UserRole_created_at` FROM `user_roles` `UserRole` WHERE `UserRole`.`id` = ? -- PARAMETERS: [8]
query: INSERT INTO `user_roles`(`id`, `user_id`, `role_id`, `created_at`) VALUES (DEFAULT, ?, ?, DEFAULT) -- PARAMETERS: [2,7]
query: SELECT `UserRole`.`id` AS `UserRole_id`, `UserRole`.`created_at` AS `UserRole_created_at` FROM `user_roles` `UserRole` WHERE `UserRole`.`id` = ? -- PARAMETERS: [9]
query: UPDATE `user_roles` SET `user_id` = ? WHERE `id` = ? -- PARAMETERS: [null,1]
query: UPDATE `user_roles` SET `user_id` = ? WHERE `id` = ? -- PARAMETERS: [null,2]
query: UPDATE `user_roles` SET `user_id` = ? WHERE `id` = ? -- PARAMETERS: [null,3]

query failed: UPDATE `user_roles` SET `user_id` = ? WHERE `id` = ? -- PARAMETERS: [null,1]

【问题讨论】:

    标签: sql many-to-many typeorm


    【解决方案1】:

    我相信您还应该对您的数据库进行额外查询并获取 relationship 数据,然后将它们分配给“主”实体,在您的情况下是 User。如您所见,更新失败,因为user_id 等于NULL。如果我是你,我会使用Active Record pattern 进行带有TypeORM 的数据库操作。由于它本身管理与 DB 的连接,因此使用起来更加简单实用。

    【讨论】:

    • 我已经在使用活动记录,并且使用用户实体查找加载了关系数据。检查 sql 日志部分,新角色数据已正确插入 user_id 和 role_id。该错误发生在应该被删除并且不再存在于关系数组中的旧数据上,但由于某种原因没有被删除但正在尝试使用 user_id null 进行更新
    • 查询显示query: UPDATE 'user_roles' SET 'user_id' = ? WHERE 'id' = ? -- PARAMETERS: [null,1]', which means that the user_id = NULL`。
    • 这是引发错误的查询,检查最后一个代码部分,它是完整的 sql 日志序列,显示正确的插入,然后用 null 更新,我认为应该是永远不会删除的发生
    • 我认为更新没有执行是因为user_id = NULL,并且删除操作是在更新一之后。
    • 这没有意义,为什么要更新应该删除的行? user_id null update 的 3 行是从关系数组中删除的角色,没有必要更新它们应该被删除。这就是为什么我怀疑关系设置有问题
    【解决方案2】:

    我发现 typeorm 目前不支持这种方式的更新。默认行为是将相关记录键设置为 null 而不是删除它,在我的情况下,此行为不起作用,因为相关具有带索引的 FK。有一个 PR 添加这个特性来删除相关记录而不是设置 null: https://github.com/typeorm/typeorm/pull/7105

    解决方法是在更新前手动删除未使用的记录

    【讨论】:

      猜你喜欢
      • 2021-12-26
      • 2020-10-09
      • 1970-01-01
      • 1970-01-01
      • 2022-01-02
      • 2021-12-23
      • 1970-01-01
      • 1970-01-01
      • 2020-06-29
      相关资源
      最近更新 更多