英文:
JPA bi-directional @OneToMany not populating Foreign Key on reference table
问题
I have two tables "Action" and "Action_Function" where the latter has a Composite Primary Key (Action_id & Sequence_nr). But when I execute repository.save(action), the value of the "Action_id" column in the "Action_Function" table inserts as null.
I am using Spring Boot 3 with Spring Data JPA (Hibernate).
Action:
Action_id (pk)
Action_Function:
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 a simple Spring Data JPA Repository. We are following DDD, and these two entities are part of the same Aggregate; hence, we can have one Repository with the root aggregate Action.
@Repository
public interface ActionRepository extends JpaRepository<Action, Integer> {
}
I appreciate some advice on how to fix this.
英文:
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
Action_id (pk)
Action_Function
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.
答案1
得分: 0
JPA新增了选项,允许在子实体中从父实体派生ID。要使子实体将父实体的ID作为其复合ID的一部分使用,只需使用@MapsId注释标记关系:
@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)
// 这将使用action.id的值由JPA设置actionId属性
@MapsId("actionId")
private Action action;
// 其他字段
}
英文:
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
}
答案2
得分: 0
终于,我找到了一个有效的解决方案。
@Entity
@Table(name = "ACTION")
public class Action implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "Action_id")
private Integer actionId;
@OneToMany(mappedBy = "action", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private Set<ActionFunction> actionFunctions = new HashSet<>();
}
@Entity
@Table(name = "ACTION_FUNCTION", schema = "dbo")
@IdClass(ActionFunctionId.class)
public class ActionFunction implements Serializable {
@Id
@ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER, optional = false)
@JoinColumn(name = "Action_id", referencedColumnName = "Action_id")
private Action action;
@Id
@Column(name = "Sequence_nr")
private Integer sequenceNr;
}
@Getter
@Setter
@EqualsAndHashCode
@NoArgsConstructor
public class ActionFunctionId implements Serializable {
private Integer action;
private Integer sequenceNr;
}
**JUnit 测试**
@Test
void createAction(){
ActionFunction actionFunction = new ActionFunction();
actionFunction.setSequenceNr(1);
Action action = new Action();
action.setActionFunctions(Set.of(actionFunction));
actionFunction.setAction(action);
Action actionSaved = actionRepository.save(action);
}
[数据库记录][1]
[1]: https://i.stack.imgur.com/KI3Ra.png
英文:
Finally, I have found a working solution.
@Entity
@Table(name = "ACTION")
public class Action implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "Action_id")
private Integer actionId;
@OneToMany(mappedBy = "action", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private Set<ActionFunction> actionFunctions = new HashSet<>();
}
@Entity
@Table(name = "ACTION_FUNCTION", schema = "dbo")
@IdClass(ActionFunctionId.class)
public class ActionFunction implements Serializable {
@Id
@ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER, optional = false)
@JoinColumn(name = "Action_id", referencedColumnName = "Action_id")
private Action action;
@Id
@Column(name = "Sequence_nr")
private Integer sequenceNr;
}
@Getter
@Setter
@EqualsAndHashCode
@NoArgsConstructor
public class ActionFunctionId implements Serializable {
private Integer action;
private Integer sequenceNr;
}
JUnit Test
@Test
void createAction(){
ActionFunction actionFunction = new ActionFunction();
actionFunction.setSequenceNr(1);
Action action = new Action();
action.setActionFunctions(Set.of(actionFunction));
actionFunction.setAction(action);
Action actionSaved = actionRepository.save(action);
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论