我正在编写一个导入,需要将两种类型的数据(父级和子级)保存到各自的表和一个链接表(parentChild)中以将它们连接起来。我知道这个错误是不言自明的,并且有很多其他人提出类似的问题 - 但这是不同的。在这里,chatGpt,在项目中搜索示例和我自己的东西,我一直无法想出可行的东西。我认为这是 FK 约束的问题,但这与我在其他地方使用的模式相同。它显然*认为*我正在发送null,但不知怎的,它并没有从正在链接的两个对象中提取id。
我无法发布实际的代码,但这里有一个重现这种情况的 noddy 版本。
DDL(在mssql中运行):
CREATE SEQUENCE Project.TableParentIdSeq START WITH 1 INCREMENT BY 1;
CREATE SEQUENCE Project.TableChildIdSeq START WITH 1 INCREMENT BY 1;
CREATE SEQUENCE Project.TableParentChildIdSeq START WITH 1 INCREMENT BY 1;
CREATE TABLE Project.TableParent
(
ParentId BIGINT NOT NULL CONSTRAINT DF_Project_TableParent_TableParentIdSeq DEFAULT (NEXT VALUE FOR Project.TableParentIdSeq),
ParentName VARCHAR(10) NOT NULL
);
CREATE TABLE Project.TableChild
(
ChildId BIGINT NOT NULL CONSTRAINT DF_Project_TableChild_TableChildIdSeq DEFAULT (NEXT VALUE FOR Project.TableChildIdSeq),
ChildName VARCHAR(10) NOT NULL
);
CREATE TABLE Project.TableParentChild
(
ParentChildId BIGINT NOT NULL CONSTRAINT DF_Project_TableParentChild_TableParentChildIdSeq DEFAULT (NEXT VALUE FOR Project.TableParentChildIdSeq),
ParentId BIGINT NOT NULL,
ChildId BIGINT NOT NULL
);
ALTER TABLE Project.TableParent
ADD
CONSTRAINT PK_TableParent PRIMARY KEY (ParentId);
ALTER TABLE Project.TableChild
ADD
CONSTRAINT PK_TableChild PRIMARY KEY (ChildId);
ALTER TABLE Project.TableParentChild
ADD
CONSTRAINT PK_TableParentChild PRIMARY KEY (ParentChildId),
CONSTRAINT FK_TableParentChild_ParentId FOREIGN KEY (ParentId) REFERENCES Project.TableParent (ParentId),
CONSTRAINT FK_TableParentChild_ChildId FOREIGN KEY (ChildId) REFERENCES Project.TableChild (ChildId);
实体(每个实体都带有@entity/getter/setter/所有参数/无参数/表):
public class ParentEntity implements Serializable {
@Id
@Column(name = "ParentId")
@GeneratedValue(generator = "TableParentIdSeq")
@SequenceGenerator(name = "TableParentIdSeq", sequenceName = "Project.TableParentIdSeq", allocationSize = 1)
private Long id;
private String parentName;
@OneToMany(cascade = {CascadeType.ALL}, mappedBy = "parent", orphanRemoval = true)
private List<ParentChildEntity> children = new ArrayList<>();
public ParentEntity(String parentName) {
this.parentName = parentName;
}
}
public class ChildEntity implements Serializable {
@Id
@Column(name = "ChildId")
@GeneratedValue(generator = "TableChildIdSeq")
@SequenceGenerator(name = "TableChildIdSeq", sequenceName = "Project.TableChildIdSeq", allocationSize = 1)
private Long id;
private String childName;
public ChildEntity(String childName) {
this.childName = childName;
}
}
public class ParentChildEntity implements Serializable {
@Id
@Column(name = "ParentChildId")
@GeneratedValue(generator = "TableParentChildIdSeq")
@SequenceGenerator(name = "TableParentChildIdSeq", sequenceName = "Project.TableParentChildIdSeq", allocationSize = 1)
private Long id;
@ManyToOne //(cascade = {CascadeType.ALL})
@JoinColumn(name = "ParentId", referencedColumnName = "ParentId", insertable = false, updatable = false)
private ParentEntity parent;
@ManyToOne
@JoinColumn(name = "ChildId", referencedColumnName = "ChildId", insertable = false, updatable = false)
private ChildEntity child;
public ParentChildEntity (ParentEntity parent, ChildEntity child) {
this.parent = parent;
this.child = child;
}
}
每个表都有自己的存储库,扩展 JpaRepository 并仅使用 save()。
服务层代码:
@Transactional
public void populateTables() {
ParentEntity parentSaved = parentRepository.saveAndFlush(new ParentEntity("Parent 1"));
ChildEntity childSaved = childRepository.saveAndFlush(new ChildEntity("Child A"));
parentChildRepository.save(new ParentChildEntity(parentSaved, childSaved));
}
如果我不将最终保存到链接表,则父级和子级将成功保存。 在调试中,我可以看到父级和子级都有 id,并且在一切就位的情况下调用了parentChild save()。 我原以为子类应该像父类一样有一个 OneToMany 列表,因为它与父类的链接表有类似的关系,但 chatGpt 另有说法。 使列可为空(不是我想要做的)只是意味着它们在与数据一起保存时设置为空。 id 生成适用于所有三个表。
运行时会在控制台中出现此错误: 无法将 NULL 值插入表“Project.TableParentChild”的“ParentId”列;列不允许为空。 INSERT 失败。
将调试中的最终 save() 评估为 saveAndFlush() 给出: 无法执行语句; SQL [不适用];约束[空]
有什么想法吗?希望我不会太愚蠢。
问题已解决。
这取决于 ParentChildEntity 中父级和子级的“insertable = false,updatable = false”。我记得我认为他们在之前的工作中看起来有点违反直觉,但这就是模式并且它有效。这里没有。