I’ve encountered an issue in a parent-child entity relationship while using Spring 6.2.2 and hibernate-core-jakarta 5.6.15.Final, Oracle Database where:
- The parent entity has a primary key generated using a sequence.
- The child entity has a composite key, which includes a foreign key reference to the parent.
- The cascade type is
SAVE_UPDATE
on the parent-child relationship.
Issue Description
When saving the parent entity, Hibernate correctly triggers inserts for both the parent and child due to the cascade setting. However, the child entity’s foreign key remains null
, causing an SQLIntegrityConstraintViolationException (ORA-01400: cannot insert NULL into (“SCHEMA_NAME”.“CHILD”.“ID”).
Expected Behavior
The parent’s primary key, generated via the sequence, should be set in the child entity’s foreign key (ID
) before insertion.
Sample Code
@Entity
@Table(name = "`PARENT`")
public class Parent implements Serializable {
@Id
@SequenceGenerator(name = "generator", sequenceName = "`SAMPLE_SEQUENCE1`" , allocationSize = 1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "generator")
@Column(name = "`ID`", nullable = false, scale = 0, precision = 10)
private Long id;
@Column(name = "`NAME`", nullable = true, length = 20)
private String name;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "parent")
@Cascade({CascadeType.SAVE_UPDATE})
private List<Child> children = new ArrayList<>();
// Getters and setters
@PostPersist
private void onPostPersist() {
if(children != null) {
children.forEach(_child -> _child.setParent(this));
}
}
}
@Entity
@Table(name = "`CHILD`")
@IdClass(ChildIdClass.class)
public class Child implements Serializable {
@Id
@Column(name = "`CHILD_ID`", nullable = false, scale = 0, precision = 10)
private Long childId;
@Id
@Column(name = "`ID`", nullable = false, scale = 0, precision = 10)
private Long id;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "`ID`", referencedColumnName = "`ID`", insertable = false, updatable = false, foreignKey = @ForeignKey(name = "`PC_FK`"))
@Fetch(FetchMode.JOIN)
private Parent parent;
public void setParent(Parent parent) {
if(parent != null) {
this.id = parent.getId();
}
this.parent = parent;
}
// Getters and setters
}
public class ChildIdClass implements Serializable {
private Long id;
private Long childId;
// Getters, setters, equals, and hashCode methods
}
Service Layer
In the service layer, the parent entity is saved as follows:
getHibernateTemplate().save(parentEntity);
getHibernateTemplate().flush();
Debug Logs & Observation
Debug logs show that when Hibernate saves the entities:
[org.hibernate.internal.util.EntityPrinter] - Listing entities:
[org.hibernate.internal.util.EntityPrinter] - com.orclcdb.Parent{id=362, name=abc, children=[com.orclcdb.Child]}
[org.hibernate.internal.util.EntityPrinter] - com.orclcdb.Child{parent=null}
[org.hibernate.SQL] - insert into "SCHEMA_NAME"."PARENT" ("ID", "NAME") values (?, ?)
[org.hibernate.SQL] - insert into "SCHEMA_NAME"."CHILD" ("CHILD_ID", "ID") values (?, ?)
[org.hibernate.engine.jdbc.spi.SqlExceptionHelper] - could not execute statement [n/a]
java.sql.SQLIntegrityConstraintViolationException: ORA-01400: cannot insert NULL into ("SCHEMA_NAME"."CHILD"."ID")
Observation
- The
@PostPersist
method inParent
is supposed to assign the generated ID to theChild
entity. - However,
@PostPersist
is called only duringflush()
, not at the time ofsave()
and during flush operation the id field in the ChildIdClass is already null so the data that is being inserted is null.
Additional Observations
This issue does not occur when:
- The parent uses
IDENTITY
generation instead ofSEQUENCE
. - The parent has no generator, meaning the ID is manually assigned before persistence.