I have two tables "Action" and "Action_Function" where later has Composite Primary Key (Action_id & Sequence_nr). But, when I execute repository.save(action) the value Action_id column on Action_Function table insert as null.
I am using Spring Boot 3 with Spring Data JPA(Hibernate).
Action_id (pk)
Action_id (pk) Sequence_nr (pk) <-- Sequence_nr comes from UI(DTO)
I am using @IdClass for the convenience of MapStruct DTO to entity mapping, hence not using @Embeddable.
@Entity
@Table(name = "ACTION")
public class Action {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "Action_id")
private Integer actionId;
@OneToMany(mappedBy = "action", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
private Set<ActionFunction> actionFunctions = new HashSet<>();
// Other fields
}
@Entity
@Table(name = "ACTION_FUNCTION")
@IdClass(ActionFunctionId.class)
public class ActionFunction {
@Id
@Column(name = "Action_id")
private Integer actionId;
@Id
@Column(name = "Sequence_nr")
private Integer sequenceNr;
// here, added insertable and updatable false otherwise JPA tries to add two Action_id
@ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER, optional = false)
@JoinColumn(name = "Action_id")
@MapsId("actionId")
private Action action;
// other fields
}
public class ActionFunctionId implements Serializable {
@Serial
private static final long serialVersionUID = -4601022716234244483L;
private Integer actionId;
private Integer sequenceNr;
// Equals & hashCode
}
Here is how I am populating entities, a simple representation of my current code where they get populated using MapStruct.
@Service
public class ActionServiceImpl {
public void saveAction(){
ActionFunction actionFunction = new ActionFunction();
actionFunction.setSequenceNr(1);
ActionFunction actionFunction2 = new ActionFunction();
actionFunction2.setSequenceNr(2);
Action action = new Action();
action.setActionFunctions(Set.of(actionFunction, actionFunction2));
Action actionSaved = actionRepository.save(action);
}
}
ActionRepository is simple Spring Data Jpa Repository. We are following DDD and these two entities are part of same Aggregate, hence we can have one Repository with root aggregate Action.
@Repository
public interface ActionRepository extends JpaRepository<Action, Integer> {
}
I really appreciate some advice how to fix this.
JPA added options to allow deriving the ID in a child entity from the parent. To have your child use the parent's ID as part of its compound ID, you just need to mark the relationship with the MapsId annotation:
@Entity
@Table(name = "ACTION_FUNCTION")
@IdClass(ActionFunctionId.class)
public class ActionFunction {
@Id
@Column(name = "Action_id")
private Integer actionId;
@Id
@Column(name = "Sequence_nr")
private Integer sequenceNr;
@ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER, optional = false)
//This will have JPA set the actionId property using the action.id value
@MapsId("actionId")
private Action action;
// other fields
}