一对一关联下contactFrequency属性无法序列化问题求助
问题分析与解决方案
核心问题定位
从错误日志和代码来看,触发500错误的原因有三个:
- 空指针异常:当未找到对应
ContactFrequency记录时,后续代码直接调用policyDocument.getContactFrequency()会触发空指针,导致序列化失败 - 循环引用导致序列化死循环:
PolicyDocument与ContactFrequency的双向一对一关联,在JSON序列化时会形成无限递归 - DTO映射逻辑错误:
toFrequencyDto方法中错误地将DTO自身的frequencyDays赋值给自己,导致数据丢失或异常
具体解决方案
1. 修复空指针与DTO映射错误
修改findById API方法,先判断contactFrequency是否为null,避免空指针;同时修正DTO映射的逻辑错误:
修改API代码
@Override @GET @Path("{id}") public PolicyDocument findById(@PathParam("id") Long id) throws NotFoundAppException { PolicyDocument policyDocument = policyFinder.retrievePolicyDocument(id); ContactFrequency contactFrequency = searchContactFrequency.findContactFrequencyByDocumentId(policyDocument); // 处理contactFrequency为null的情况 if (contactFrequency != null) { ContactFrequencyDto contactFrequencyDto = searchContactFrequency.toFrequencyDto(contactFrequency); if (contactFrequencyDto.getFrequencyDays() != null) { contactFrequencyDto.setId(null); pmLogger.info("Policy Document's contact frequency: " + contactFrequencyDto); } policyDocument.setContactFrequency(contactFrequency); } else { pmLogger.info("No contact frequency found for policy document: " + id); // 设置空对象避免后续空指针 policyDocument.setContactFrequency(new ContactFrequency()); } pmLogger.info("Policy Document with contactFrequency: " + policyDocument); if (PolicyDocumentStatus.DRAFT.equals(policyDocument.getStatus()) && (currentEnvironmentUserRoles.hasAnyRole(Roles.Use_PM2_POLICY_APPROVER) && !currentEnvironmentUserRoles.hasAnyRole(Roles.Use_PM2_POLICY_USER, Roles.Use_PM2_POLICY_ADMIN, Roles.Use_PM2_GLOBAL_ADMIN))) { throw new NotFoundAppException("You do not have the permission to access the document"); } return policyDocument; }
修正toFrequencyDto方法
public ContactFrequencyDto toFrequencyDto(ContactFrequency contactFrequency) { if (contactFrequency == null) { return new ContactFrequencyDto(); // 返回空DTO避免空指针 } ContactFrequencyDto contactFrequencyDto = new ContactFrequencyDto(); contactFrequencyDto.setId(contactFrequency.getId()); // 修正:使用实体的frequencyDays赋值给DTO contactFrequencyDto.setFrequencyDays(contactFrequency.getFrequencyDays()); pmLogger.info("Mapping entity to dto completed successfully: " + contactFrequencyDto); return contactFrequencyDto; }
2. 解决循环引用序列化问题
由于项目使用Yasson(Eclipse JSON-B)进行序列化,通过以下方式解决双向关联的循环引用:
在ContactFrequency的policyDocument字段添加忽略注解
@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER) @JoinColumn(name = "policy_document_id", referencedColumnName = "id", nullable = false) @JsonbTransient // 序列化时忽略该字段,避免循环引用 private PolicyDocument policyDocument;
或者使用JSON-B双向引用注解:
// 在PolicyDocument的contactFrequency字段添加 @OneToOne(mappedBy ="policyDocument", cascade = CascadeType.ALL, fetch = FetchType.EAGER) @Nullable @JsonbManagedReference protected ContactFrequency contactFrequency; // 在ContactFrequency的policyDocument字段添加 @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER) @JoinColumn(name = "policy_document_id", referencedColumnName = "id", nullable = false) @JsonbBackReference private PolicyDocument policyDocument;
3. 优化JPA关联查询(可选)
为避免N+1查询问题,修改retrievePolicyDocument方法的查询逻辑,添加JOIN FETCH直接关联查询ContactFrequency:
// 示例JPQL查询 SELECT p FROM PolicyDocument p JOIN FETCH p.contactFrequency WHERE p.id = :id
内容的提问来源于stack exchange,提问作者Yiğit Güneş
相关产品推荐
相关产品推荐

