首页 > 解决方案 > 无法删除或更新父行:更新外键时外键约束失败

问题描述

我有一个实体成员

public class Member implements Serializable {
    @Id
    @Column(nullable = false, updatable = true)
    private String email;
    private String name;
    private String surname;
    private String password;
    private String phone;
    private String url_image;

    // posts the user has made
    @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    private Collection<Post> posts = new ArrayList<>();

    // load the roles everytime we load a user
    @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    private Collection<Role> roles = new ArrayList<>();

    // foreign key relationships
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "member", orphanRemoval = true)
    private Collection<Likes> like;
    
    // constructor, getters, setters
    
}

实体角色

public class Role {
    @Id @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String name;

}

和一个实体 Post:

public class Post implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String text;
    private Date date;
    private String post_name;
    private String post_surname;
}

实体 Member 与 Post 和 Roles 具有 OneToMany 关系,我希望当成员的电子邮件更新时,其他实体上的相应记录也具有更新的外键。此外,还有另一种情况,称为 Likes 的实体:

public class Likes implements Serializable {
    @Id
    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "post_id", referencedColumnName = "id")
    private Post post_id;

    @Id
    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "email", referencedColumnName = "email")
    private Member member;

}

其中关系由成员映射到喜欢。在我在 MyQSL 上运行更新查询的所有情况下,我都会收到错误消息

Cannot delete or update a parent row: a foreign key constraint fails(`members`.`member_posts`, CONSTRAINT `FKbd5r2qwucjyiwbv538j3edjad` FOREIGN KEY (`member_email`) REFERENCES `member` (`email`))

要么来自由 Member 和 Post 的关系构成的 member_posts 表,要么来自由 Member 和 Roles 的关系或 Likes 表构成的 member_roles 表。任何人都知道我在这里搞砸了什么,因为我真的无法弄清楚。

标签: mysqlspring-bootspring-data-jpa

解决方案


如果我对您的理解正确,您已经使用用户的电子邮件地址作为从子表到父表的外键引用。现在您需要更新电子邮件地址。

外键是数据完整性约束。它们的存在是为了确保子行始终具有对父行的有效引用。因此,您无法更改父列,因为子列将不再具有有效引用,并且您无法更改子列,因为它将指向不存在的父列

(可以将子电子邮件地址设置为现有但不正确的父电子邮件,因此可以应对在父表上设置唯一索引并首先更新父表。然后父更新将由于重复而失败。)

幸运的是,您可以通过在执行更新时禁用外键约束来解决所有这些问题:

SET FOREIGN_KEY_CHECKS = 0;

当然,一旦更新完成,您需要再次启用检查:

SET FOREIGN_KEY_CHECKS = 1;

但是如果出现问题怎么办?

如果由于某种原因更新失败并且外键检查被禁用,您可以让您的数据库处于未知状态。为了解决这个问题,将整个更新包装在一个事务中:

在伪代码中:

SET FOREIGN_KEY_CHECKS = 0;
START TRANSACTION;
UPDATE parentTable set email = 'newEmail@example.com' where email = 'oldEmail@example.com';
UPDATE childTable set email = 'newEmail@example.com' where email = 'oldEmail@example.com';

IF (No error hos occurred)
    COMMIT
ELSE
    ROLLBACK
ENDIF

SET FOREIGN_KEY_CHECKS = 1;

这就是 MySQL 的解决方案。使用可变数据作为外键约束可能是一个糟糕的选择。我会考虑用包含父 ID 的列替换电子邮件外键并在其上设置外键约束。这不应该改变,然后可以随意更新电子邮件地址。

不幸的是,春天不是我的菜。这是解决方案,但您必须自己实施。


推荐阅读