如何在一对一关系中使用 onDelete: 'CASCADE'

Posted

技术标签:

【中文标题】如何在一对一关系中使用 onDelete: \'CASCADE\'【英文标题】:How to use onDelete: 'CASCADE' on one-to-one relationship如何在一对一关系中使用 onDelete: 'CASCADE' 【发布时间】:2021-01-08 03:49:35 【问题描述】:

当用户被删除时,我尝试删除用户的个人资料。但它不会删除个人资料上的任何内容。

用户实体

@Entity()
export class User 

    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    name: string;

    @OneToOne(type => Profile, profile => profile.user, 
               eager: true, cascade: true, onDelete: 'CASCADE' )
    @JoinColumn()
    profile: Profile;


个人资料实体

@Entity()
export class Profile 

    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    gender: string;

    @Column()
    photo: string;

    @OneToOne(type => User, user => user.profile)
    user: User;


我如何删除用户。

const userRepository = connection.getRepository(User);
const user = await userRepository.findOne( where: id: 'someuserId', relations: ["profile"] );

await user.remove()

用户已被删除,但用户的个人资料没有任何反应。

谁能解释一下如何删除一对一的关系?

【问题讨论】:

如果您查看 SQL 数据库中的实际表,是否配置了实际的级联? 这里是用户 --> 约束 FK_ef7a0cc61d7873284f1cedc5d5d 外键 (profileId) 引用 profile (id) 删除级联 还有这个 --> 唯一键 REL_ef7a0cc61d7873284f1cedc5d5 (profileId) @AluanHaddad 在 SQL 中,cascade 规范属于关系中的子表或依赖表。我假设Profile 依赖于User,因此您可能需要将级联规范放在装饰器中关系的另一端。 user.remove() 这个语法正确吗?用户实体上不存在删除。 【参考方案1】:

用户

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

    @Column()
    name: string;

    @OneToOne(type => Profile, profile => profile.user, 
               eager: true, onDelete: 'CASCADE' )
    @JoinColumn()
    profile: Profile;

个人资料

@Entity()
export class Profile 

    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    gender: string;

    @Column()
    photo: string;

    @OneToOne(type => User, user => user.profile,  onDelete: "CASCADE" )
    user: User;


我运行此代码以从两个实体中删除条目。

const userRepository = connection.getRepository(User);
const user = await userRepository.findOne( where: id: 'someuserId' );

const profileRepository = connection.getRepository(Profile);
const profile = await profileRepository.findOne( where: id: user.profile.id );

await profileRepository.remove(profile)

这是一对一关系的预期行为。 必须删除引用方才能使级联删除生效。讨论link

编辑:在github

上提出了问题

【讨论】:

我尝试在两个实体中定义 onDelete: "CASCADE"。配置文件仍然没有删除。我应该将其更改为多对一吗? 对不起,我忘了提几点。再次检查我的答案 这是一个非常违反直觉的 API。在两端指定它会使代码看起来好像删除任何一端都会级联删除另一端,我假设如果删除了拥有的实体,则所有者不会被删除。 可悲的是,它就是这样工作的。我会在 typeorm github 上提出一个问题。【参考方案2】:

解决方案

我最近发现自己在同一个地方。我在我的数据库中创建了一个继承关系。多个实体引用一个公共实体,该实体为每个专用表提供基本属性。例如:通用属性持有者表“Car”和专用表“Truck”、“Bus”。现在我想在删除“卡车”或“公共汽车”时删除相应的“汽车”实体。

在 typeorm 中,专用表应该引用公共属性表。要使 CASCADE DELETE 工作,您必须使用 @JoinColumn() 注释指定持有 id 的一侧。然后你必须在@JoinColumn()注解的同一侧的一对一关系上指定onDelete: "CASCADE"

要验证创建的表,您可以在数据库中使用SHOW CREATE TABLE table_name。在那里,您将看到 CASCADE DELETE 是否存在于正确的外键约束上。 (它应该在持有外键的桌子上)。

也就是说,两边都不需要设置CASCADE DELETE

代码示例

(参见 Gaurav Sharma 的回答 - 已修改)


// referenced table
@Entity()
export class User 
    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    name: string;

    // you can use eager on the none-owning side
    // this can be very helpful for inheritance
    @OneToOne(type => Profile, profile => profile.user,  eager: true )
    profile: Profile;



// owning side - foreign key in this table
@Entity()
export class Profile 

    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    gender: string;

    @Column()
    photo: string;

    // specify onDelete here because owning side FK constraint would fail 
    // and it needs to know what is going to happen
    @OneToOne(type => User, user => user.profile,  onDelete: "CASCADE" )
    @JoinColumn()
    user: User;


从这里开始,您将创建一个新用户。在配置文件中设置保存的用户以保存并保存配置文件。 删除配置文件不会删除用户。 删除用户将删除个人资料。

我还从双方调查了CASCADE DELETE。根据我的发现,您必须在两侧指定 @JoinColumn() 注释,并在两侧声明 onDelete: "CASCADE"。这将需要至少两次保存新实体,并且我在文档中没有找到有关双向CASCADE DELETE 的更多信息。在这些情况下,我会考虑手动删除,因为可能会变得非常不清楚将会发生什么以及何时需要保存实体。

【讨论】:

【参考方案3】:

我解决了我的问题(暂时)

我只是在两个实体中手动删除。

const userRepository = connection.getRepository(User);
const user = await userRepository.findOne( where: id: 'someuserId', relations: ["profile"] );

const profileRepository = connection.getRepository(Profile);
const profile = await profileRepository.findOne(where: id: user.profile.id).getOne()

await profile.remove()
await user.remove()

【讨论】:

再次查看我的答案。我在我的机器上运行了该代码。

以上是关于如何在一对一关系中使用 onDelete: 'CASCADE'的主要内容,如果未能解决你的问题,请参考以下文章

当 ForEach 中发生 onDelete 时如何更新其他 CoreData?

在 symfony/doctrine 的 schema.yml 中,我应该在哪里放置 onDelete: CASCADE 用于多对多关系?

Laravel 5 删除一对多关系

EFCore 可为空的关系设置 onDelete:ReferentialAction.Restrict

如何禁用 .onDelete- 或如何在列表中逐行使用 .deleteDisabled?

EF Core - 为啥 ClientSetNull 是可选关系的默认 OnDelete 行为(而不是 SetNull)