由于键更改,尝试插入时外键约束失败

Posted

技术标签:

【中文标题】由于键更改,尝试插入时外键约束失败【英文标题】:Foreign key constraint failure when trying to insert because of key change 【发布时间】:2020-02-26 09:20:44 【问题描述】:

我有一个 Content 对象,它引用了 ManyToMany 关系中的一组 Tag 对象。作为持久化新内容对象的一部分,我在 PostgreSQL 中查看标签是否已经存在,如果存在,那么我将对它的引用添加到内容对象并尝试保存内容对象。我遇到的问题是,当我这样做时,我将标签与 id 相关联,例如1 和 2 与内容,当我尝试保存内容时,它因外键违规而失败,因为它说 id 为 10 的标签不存在。我逐步浏览了代码,当我将标签添加到与内容关联的标签集时,它具有正确的 id(即 1 和 2),那么为什么当我保存它时它会更改为不同的 id?我正在使用 JPA、Hibernate、Spring 和 PostgreSQL。我在下面附加了休眠内容对象。 “content_tag”表仅由 content_id 和 tag_id 组成,它们是对内容和标签表的外键引用。澄清一下:此实例中的标签已经创建,我只是通过在数据库中搜索它们并将它们的关联添加到内容来添加关联

这是错误:

org.postgresql.util.PSQLException: ERROR: insert or update on table "content_tag" violates foreign key constraint "tag_id_fkey"
  Detail: Key (tag_id)=(12) is not present in table "tag".

显示尝试保存的代码:

for (final String tag : request.getContent().getTags()) 
    final List<Tag> current = tagRepository.findByName(tag);
    if (current.isEmpty()) 
        final Tag newTag = new Tag(tag);
        tagRepository.save(newTag);
        content.addTag(newTag);
     else 
        content.addTag(current.get(0));
    


final Content response = contentRepository.save(content);

这是内容类:

@Entity(name = "Content")
@Table(name = "content")
@TypeDef( name= "StringJsonObject", typeClass = StringJsonUserType.class)
public class Content implements Serializable 

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Type(type = "StringJsonObject")
    @Column(name = "text")
    private String text;

    public Long getId() 
        return id;
    

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

    public String getText() 
        return text;
    

    public void setText(String text) 
        this.text = text;
    

    @ManyToMany(cascade = 
            CascadeType.PERSIST,
            CascadeType.MERGE
    )
    @JoinTable(name = "content_tag",
            joinColumns = @JoinColumn(name = "tag_id"),
            inverseJoinColumns = @JoinColumn(name="content_id")
    )
    private Set<Tag> tags = new HashSet<>();

    public Set<Tag> getTags() 
        return tags;
    

    public void setTags(Set<Tag> tags) 
        this.tags = tags;
    

    public void addTag(Tag tag) 
        tags.add(tag);
        tag.getContents().add(this);
    

    public void removeTag(Tag tag) 
        tags.remove(tag);
        tag.getContents().remove(this);
    

    @Override
    public boolean equals(Object o) 
        if (this == o) return true;
        if (!(o instanceof Content)) return false;
        return id != null && id.equals(((Content) o).getId());
    

    @Override
    public int hashCode() 
        return 31;
    

标签实体:

@Entity(name = "Tag")
@Table(name = "tag")
public class Tag implements Serializable 

    @Id
    @GeneratedValue
    private Long id;

    @Column(name = "name")
    private String name;

    @ManyToMany(mappedBy = "tags")
    private Set<Content> contents = new HashSet<>();

    public Tag() 

    public Tag(String name) 
        this.name = name;
    

    public Long getId() 
        return id;
    

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

    public String getName() 
        return name;
    

    public void setName(String name) 
        this.name = name;
    

    public Set<Content> getContents() 
        return contents;
    

    public void setContents(Set<Content> contents) 
        this.contents = contents;
    

    @Override
    public boolean equals(Object o) 
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Tag tag = (Tag) o;
        return Objects.equals(name, tag.name);
    

    @Override
    public int hashCode() 
        return Objects.hash(name);
    

【问题讨论】:

首先检查您是否正在使用事务。而且这里最好提供标签实体。 【参考方案1】:

将内容实体中的关系配置更改为:

@JoinTable(name = "content_tag",
        joinColumns = @JoinColumn(name = "content_id"),
        inverseJoinColumns = @JoinColumn(name="tag_id")
)

【讨论】:

【参考方案2】:

这是事务造成的,U无法读取未提交的数据,U可能会在U创建一行'tag'后提交事务。

【讨论】:

在此事务之前,在这种情况下,标签已经持久化。我正在从数据库中读取它们,而不是在这个实例中创建新的

以上是关于由于键更改,尝试插入时外键约束失败的主要内容,如果未能解决你的问题,请参考以下文章

无法将值插入表中,外键约束不断失败

在 My SQL 中不断收到外键约束失败消息

由于外键约束,使用 phpMyAdmin 复制数据库失败

sql中怎样创建外键约束

sql server怎样删除外键约束?

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