I’m writing an import and need to save two types of data (parent & child) to their respective tables and a link table (parentChild) to join them up. I know the error is pretty self explanatory and there have been lots of other people asking similar questions – but this is different. Between here, chatGpt, searching the project for examples and my own stuff I haven’t been able to come up with something that works. I think it’s a problem with the FK constraint but it’s the same pattern I’ve used elsewhere. It clearly *thinks *I’m sending null but somehow it’s not pulling the ids from the two objects being linked.
I can’t post the actual code but here’s a noddy version that recreates the situation.
DDL (run in 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);
Entities (each with @entity/getter/setter/all args/no args/table):
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;
}
}
Each table has its own repo extending JpaRepository and using only the save().
Service layer code:
@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));
}
If I don’t do that final save to the link table the parent and child are saved successfully.
In debug I can see that both parent and child have ids and the parentChild save() is being called with everything in place.
I had thought the child class should have a OneToMany list as well like the parent, as it has a similar relationship to the link table as the parent but chatGpt said otherwise.
Making the columns nullable (not what I want to do) just means they get set to null when saved with data.
The id generation works on all three tables.
Running that gives this error in the console:
Cannot insert the value NULL into column ‘ParentId’, table ‘Project.TableParentChild’; column does not allow nulls. INSERT fails.
Evaluating that final save() in debug as a saveAndFlush() gives:
could not execute statement; SQL [n/a]; constraint [null]
Any ideas? Hopefully nothing too stupid on my part.
damanara is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.