如何解决无法添加或更新子行:Spring JPA 中的外键约束失败错误?

Posted

技术标签:

【中文标题】如何解决无法添加或更新子行:Spring JPA 中的外键约束失败错误?【英文标题】:How to solve Cannot add or update a child row: a foreign key constraint fails error in Spring JPA? 【发布时间】:2021-09-26 12:10:03 【问题描述】:

我正在研究 Spring Boot api,其中我有一个实体模型课程,另一个是主题。 在一个课程下,我想要一个包含多个科目的列表。我正在使用 OneToMany 注释来执行此操作。

这是我的代码:

course.java

@Entity
@EntityListeners(AuditingEntityListener.class)
public class course 

    @Override
    public String toString() 
        return "course [id=" + id + ", title=" + title + ", description=" + description + "]";
    

    public long getId() 
        return id;
    

    public void setId(int id) 
        this.id = id;
    

    public String getTitle() 
        return title;
    

    public void setTitle(String title) 
        this.title = title;
    

    public String getDescription() 
        return description;
    

    public void setDescription(String description) 
        this.description = description;
    
   
    
    public List<subject> getSubjects() 
        return subjects;
    

    public void setSubjects(List<subject> subjects) 
        this.subjects = subjects;
    

    public String getProperty(String key) 
        switch (key) 
        case "title":

            return this.title;
        case "description":
            return this.description;
        case "date":
            return this.date.toString();
        case "id":
            return Integer.toString(this.id);

        default:
            return "helo";
        
    

    public course() 
        super();
        // TODO Auto-generated constructor stub
    



    public course(int id, String title, List<subject> subjects, String description, Date date) 
        super();
        this.id = id;
        this.title = title;
        this.subjects = subjects;
        this.description = description;
        this.date = date;
    



    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    private String title;
    @OneToMany
    private List<subject> subjects;
    private String description;
    @CreatedDate
    @Temporal(TemporalType.DATE)
    private Date date;

    public Date getDate() 
        return date;
    

    public void setDate(Date date) 
        this.date = date;
    

subject.java

@Entity
public class subject 
   public String getName() 
        return name;
    
    public void setName(String name) 
        this.name = name;
    
    public String getComplexity() 
        return complexity;
    
    public void setComplexity(String complexity) 
        this.complexity = complexity;
    
public subject(String name, String complexity) 
        super();
        this.name = name;
        this.complexity = complexity;
    
@Id
   private String name;
   private String complexity;

现在当我发送POST 请求时:


    "title":"test course sossk",
    "description":"Is it",
    "subjects":[
        
            "name":"java",
            "complexity":"easy"
        ,
        
            "name":"c++",
            "complexity":"easy"
        
    ]

我收到这样的错误:

 SQL Error: 1452, SQLState: 23000
2021-07-18 20:01:48.818 ERROR 10408 --- [nio-8080-exec-2] o.h.engine.jdbc.spi.SqlExceptionHelper   : Cannot add or update a child row: a foreign key constraint fails (`courses`.`course_subjects`, CONSTRAINT `FKgy72njp8nmip43dy1cwk43ue6` FOREIGN KEY (`subjects_name`) REFERENCES `subject` (`name`))
2021-07-18 20:01:48.819  INFO 10408 --- [nio-8080-exec-2] o.h.e.j.b.internal.AbstractBatchImpl     : HHH000010: On release of batch it still contained JDBC statements
2021-07-18 20:01:48.861 ERROR 10408 --- [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.DataIntegrityViolationException:

我该如何解决这个问题?如何添加外键?

任何帮助将不胜感激。

Update

course.java

@Entity
@EntityListeners(AuditingEntityListener.class)
public class course 

    @Override
    public String toString() 
        return "course [id=" + id + ", title=" + title + ", description=" + description + "]";
    

    

    public int getId() 
        return id;
    



    public void setId(int id) 
        this.id = id;
    



    public String getTitle() 
        return title;
    



    public void setTitle(String title) 
        this.title = title;
    







    public List<subject> getSubjects() 
        return subjects;
    



    public void setSubjects(List<subject> subjects) 
        this.subjects = subjects;
    



    public String getDescription() 
        return description;
    



    public void setDescription(String description) 
        this.description = description;
    



    public String getProperty(String key) 
        switch (key) 
        case "title":

            return this.title;
        case "description":
            return this.description;
        case "date":
            return this.date.toString();
        case "id":
            return Integer.toString(this.id);

        default:
            return "helo";
        
    

    public course() 
        super();
        // TODO Auto-generated constructor stub
    
public course(int id, String title, List<subject> subjects, String description, Date date) 
        super();
        this.id = id;
        this.title = title;
        this.subjects = subjects;
        this.description = description;
        this.date = date;
    














    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    private String title;
    @OneToMany(targetEntity = subject.class,cascade = CascadeType.ALL)
    private List<subject> subjects;
    private String description;
    @CreatedDate
    @Temporal(TemporalType.DATE)
    private Date date;

    public Date getDate() 
        return date;
    

    public void setDate(Date date) 
        this.date = date;
    

subject.java

@Entity
public class subject 
   public String getName() 
        return name;
    
    public void setName(String name) 
        this.name = name;
    
    public String getComplexity() 
        return complexity;
    
    public void setComplexity(String complexity) 
        this.complexity = complexity;
    
public subject(String name, String complexity) 
        super();
        this.name = name;
        this.complexity = complexity;
    
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
  private int id;
   private String name;
   private String complexity;
   

【问题讨论】:

缺少拥有方的@ManyToOne-注解(即subject)。我建议阅读有关该主题的教程,例如this one over at Baeldung。 --- 两点说明:在构造函数中对super() 的显式调用是多余的,如果不存在对super(...)this(...) 的显式调用,它们会被隐式插入。 - Java 中的类应始终以大写字母开头(course -> Coursesubject -> Subject)。 你能指导我使用上面的简单实现吗? 对不起,我们不是代码编写服务。我不会提供实现。如果我要提供一个实现,它将使您无法自己解决它。从长远来看,自己解决挑战应该会让你受益更多。 不用担心,谢谢我自己试试 【参考方案1】:
 @OneToMany(mappedBy="name")
 private List<subject> subjects;


 @ManyToOne
 @JoinColumn(name="course_id", nullable=false) // should reference mapped column
 private String name;

您的解决方案将是这样的。 也请参考OneToMany & ManyToOne mapping JPA / Hibernate

【讨论】:

我完成了解决方案的第一部分,实施工作正常。我应该使用第二部分,即ManyToOne 部分有什么具体原因吗? @OneToMany 注解用于定义 Subject 类中的属性,该属性将用于映射 mappedBy 变量。 我理解,但不使用 ManyToOne 我的 api 工作完美。ManyToone 部分用于什么? 能否更新您的新代码? 如果关系是双向的,则必须使用mappedBy 元素来指定作为关系所有者的实体的关系字段或属性。您的案例targetEntity(作为关联目标的实体类)已定义,因此对我来说似乎是正确的。【参考方案2】:

请参阅下面和评论中的说明。我使用 lombok @Data 来缩短 getter、setter、构造函数、tostring 的代码。

// Course.java
@Entity
@Data
@EntityListeners(AuditingEntityListener.class)
public class Course 
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    
    @OneToMany(
      cascade = CascadeType.ALL,
      fetch = FetchType.EAGER,
      mappedBy = "course", // we map the connection field in Subject entity
      orphanRemoval = true // will delete Subjects linked
    )
    private List<Subject> subjects;

    private String title;
    private String description;
    @CreatedDate
    @Temporal(TemporalType.DATE)
    private Date date;


// Subject.java
@Entity
@Data
public class Subject 
   @Id
   @GeneratedValue(strategy=GenerationType.AUTO)
   private int id;
   
   // Here we complete the connection/relationship to the course
   @ManyToOne
   @JoinColumn(
     name = "course_id",
     nullable = false,
     referencedColumnName = "id", // this `id` is the Course.id
     foreignKey = @ForeignKey(name = "course_subject_fk")
   )
   private Course course;

   private String name;
   private String complexity;
   

总结一下关系:一个课程可以有很多科目,但一个科目只能分配给一个课程。

编辑 我在这里所做的是使关系成为双向的。意思是说,当您获取课程时,它会使用fetch = FetchType.EAGER 自动获取主题。如果您不希望这种行为,可以将值更改为 FetchType.LAZY。在获取主题时也会返回课程。如果你也不想这样,可以使用龙目岛的@ToString(exclude = "course")

【讨论】:

以上是关于如何解决无法添加或更新子行:Spring JPA 中的外键约束失败错误?的主要内容,如果未能解决你的问题,请参考以下文章

如何修复“无法添加或更新子行:外键约束失败”[重复]

C# - 无法添加或更新子行:外键约束失败

#1452 - 无法添加或更新子行:外键约束失败

:完整性约束违规:1452 无法添加或更新子行:laravel 迁移中的外键约束失败

如何修复部署到Linux服务器后MySql中的“无法添加或更新子行:外键约束失败”?

Hibernate:无法添加或更新子行:外键约束失败