具有父复合 pk 的 JPA OneToMany 是子主键派生实体问题的一部分
Posted
技术标签:
【中文标题】具有父复合 pk 的 JPA OneToMany 是子主键派生实体问题的一部分【英文标题】:JPA OneToMany with Parent composite pk is part of child primary key Derived Entity issue 【发布时间】:2021-11-09 00:03:09 【问题描述】:一对多与父复合主键是子主键问题的一部分。使用以下代码 sn-ps 引发异常
示例 JSON 嵌入数据如下:
"pt_reg_no": "1000", //序列号生成 "game_year": "G12021", “名字”:“我的名字”, “事件详情”:[ "major_event_code": "A", "sub_event_code": "A7", “category_code”:“MO” , "major_event_code": "B", "sub_event_code": "B7", “category_code”:“WO” ]
参与者可以注册多个活动: 参与者(复合键) - pt_reg_no, game_year EventDetails(复合键) - pt_reg_no、game_year、sub_event_code、category_code
//Parent Class IDclass
public class ParticipantKey implements Serializable
private Long pt_reg_no;
private String game_year;
@IdClass(ParticipantKey.class)
public class ParticipantModel
@Id
@GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "participantseq"
)
@SequenceGenerator(
name = "participantseq",
allocationSize = 1
)
private Long pt_reg_no;
@Id
private String game_year = 'G12021';
@OneToMany(mappedBy = "participantModel", orphanRemoval = true, cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Set<EventDetailsModel> eventDetails = new HashSet<>();
public Set<EventDetailsModel> getEventDetails()
return eventDetails;
public void setEventDetails(Set<EventDetailsModel> eventDetails)
this.eventDetails = eventDetails;
for (EventDetailsModel b : eventDetails)
b.setParticipantModel(this);
//Child class IDclass
public class ParticipantEventDetailsId implements Serializable
private ParticipantKey participantModel;
private String sub_event_code;
private String category_code;
public class EventDetailsModel
@Id
@Column(name = "sub_event_code")
private String sub_event_code;
@Id
@Column(name = "category_code")
private String category_code;
@Id
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumns(
@JoinColumn(name = "pt_reg_no", referencedColumnName = "pt_reg_no"),
@JoinColumn(name = "game_year", referencedColumnName = "game_year")
)
private ParticipantModel participantModel;
public ParticipantModel getParticipantModel()
return participantModel;
public void setParticipantModel(ParticipantModel participantModel)
this.participantModel = participantModel;
//---------------------------------------------------
//JSON input data received into ParticipantModel from @requestBody
public class FormDataObjects
private ParticipantModel formData;
@JsonCreator
public FormDataObjects(ParticipantModel formData)
this.formData = formData;
//Controller Entry point
@RequestMapping(path = "/participantData", method = RequestMethod.POST)
@Transactional
public ResponseEntity<?> participantRegistration(@Valid @RequestBody FormDataObjects values)
ParticipantModel participant = values.getFormData();
participant.setGame_year(new SimpleDateFormat("yyyy").format(new Date()) + GenricData.game);
ParticipantModel person = participantRepository.save(participant);
if (person == null)
return ResponseEntity.ok("Participant is not inserted");
else
Set<EventDetailsModel> eventDetails = participant.getEventDetails();
if (eventDetails.size() > 0)
eventDetails.forEach(event ->
//event.setPt_reg_no(person.getPt_reg_no());
//event.setGame_year(person.getGame_year());
//event.setParticipantModel(participant);
entityManager.persist(event);
entityManager.flush();
entityManager.clear();
);
return ResponseEntity.ok("inserted");
【问题讨论】:
【参考方案1】:您可以通过像这样映射您的详细信息类来尝试“派生身份”:
public class ParticipantEventDetailsId implements Serializable
private String sub_event_code;
private String category_code;
private ParticipantKey participantModel; // matches name of attribute and type of ParticipantModel PK
public class EventDetailsModel
@Id
@Column(name = "sub_event_code")
private String sub_event_code;
@Id
@Column(name = "category_code")
private String category_code;
@Id
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumns(
@JoinColumn(name = "pt_reg_no", referencedColumnName = "pt_reg_no"),
@JoinColumn(name = "game_year", referencedColumnName = "game_year")
)
private ParticipantModel participantModel;
在第 2.4.1 节的JPA 2.2 spec 中讨论了派生的身份(带有示例)。
【讨论】:
ParticipantModel [pt_reg_no=1000, game_year=G12021,name=bapuji, eventDetails=[EventDetailsModel [major_event_code=A, state_abbr=null, sub_event_code=A7, category_code=MO]]] 谢谢@Brian下面的异常嵌套异常是 org.hibernate.HibernateException: No part of a complex identifier may be null] 有根本原因 @BapujiNakka,可能是您调用participant.getEventDetails()
返回的参与者详细信息不包含指向ParticipantModel 的反向指针(即EventDetilasModel.participantModel
是null
)。
是的@Brian,它只为空,如下所示:- ParticipantModel [pt_reg_no=null, game_year=202126, name=Bapuji, eventDetails=[EventDetailsModel [major_event_code=A, state_abbr=null, sub_event_code=A7 , category_code=MO ,participantModel=null]]] 1)如何将反向指针设置为父级。在我的问题 ABOVE 中更新了 getter 和 setter。你能看看有什么问题吗? 2) 父级中的 pt_reg_no 序列号也由于某些冲突而没有生成【参考方案2】:
OneToMany 映射的替代解决方案当父级具有复合键并且父级也需要生成序列号
在设置或列出子实体时使用 @Transient 注释 1) 使用上述注释保存父实体有助于 jpa 从保存中跳过 EventsDetailsModel 列表/集 2) 迭代 Child 并获取已保存的 Parent 复合键值,并将子数据与子键属性一起持久化。
//Parent Class IDclass
public class ParticipantKey implements Serializable
private Long pt_reg_no;
private String game_year;
@IdClass(ParticipantKey.class)
public class ParticipantModel
@Id
@GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "participantseq"
)
@SequenceGenerator(
name = "participantseq",
allocationSize = 1
)
private Long pt_reg_no;
@Id
private String game_year = 'G12021';
@Transient //Find exact path for Transient
private Set<EventDetailsModel> eventDetails = new HashSet<>();
public Set<EventDetailsModel> getEventDetails()
return eventDetails;
//Child class IDclass
public class ParticipantEventDetailsId implements Serializable
private Long pt_reg_no;
private String game_year;
private String sub_event_code;
private String category_code;
public class EventDetailsModel
@Id
@Column(name = "sub_event_code")
private String sub_event_code;
@Id
@Column(name = "category_code")
private String category_code;
@Id
private Long pt_reg_no;
@Id
private String game_year;
//---------------------------------------------------
//JSON input data received into ParticipantModel from @requestBody
public class FormDataObjects
private ParticipantModel formData;
@JsonCreator
public FormDataObjects(ParticipantModel formData)
this.formData = formData;
//Controller Entry point
@RequestMapping(path = "/participantData", method = RequestMethod.POST)
@Transactional
public ResponseEntity<?> participantRegistration(@Valid @RequestBody FormDataObjects values)
ParticipantModel participant = values.getFormData();
participant.setGame_year(new SimpleDateFormat("yyyy").format(new Date()) + GenricData.game);
ParticipantModel person = participantRepository.save(participant);
if (person == null)
return ResponseEntity.ok("Participant is not inserted");
else
Set<EventDetailsModel> eventDetails = participant.getEventDetails();
if (eventDetails.size() > 0)
eventDetails.forEach(event ->
event.setPt_reg_no(person.getPt_reg_no());
event.setGame_year(person.getGame_year());
entityManager.persist(event);
entityManager.flush();
entityManager.clear();
);
return ResponseEntity.ok("inserted");
【讨论】:
以上是关于具有父复合 pk 的 JPA OneToMany 是子主键派生实体问题的一部分的主要内容,如果未能解决你的问题,请参考以下文章
JPA 2.0 (Hibernate) 使用 @JoinTable 为 @OneToMany 生成不正确的连接表 PK
单向 OneToMany JPA 仅向我显示第一个相关对象“n”次
Spring Data JPA:保存嵌套的 OneToMany 子级(具有级联)返回 NULL 父级外部对象