JPA OneToOne关联序列化栈溢出及DTO映射失效问题求助
问题解决步骤
1. 修复实体类基础错误
你的Person.java类名误写为Personal,但Address实体和PersonService中引用的都是Person,这会直接导致JPA关联映射失败,是关联字段为null的核心原因之一。同时Address的主键配置也存在冲突,一并修正:
修正Person实体
@Entity @Getter @Setter @RequiredArgsConstructor public class Person implements Serializable { // 类名从Personal改为Person @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private String personName; private String personLastName; // 主控方,拥有外键,无需mappedBy @OneToOne(cascade = CascadeType.ALL) private Address address; }
修正Address实体
@MapsId表示用关联Person的ID作为自身主键,因此不需要再给Address的ID添加@GeneratedValue:
@Entity @AllArgsConstructor @NoArgsConstructor @Getter @Setter public class Address implements Serializable { @Id private Integer id; // 去掉@GeneratedValue,ID由关联的Person决定 @Column(name = "Street") private String street; @Column(name = "Alley") private String alley; @Column(name = "District") private String district; // 被控方,mappedBy指定主控方的属性名,@MapsId绑定主键 @OneToOne(fetch = FetchType.EAGER, mappedBy = "address") @MapsId private Person person; public Address(String street, String alley, String district, Person person) { this.street = street; this.alley = alley; this.district = district; this.person = person; } }
2. 解决序列化栈溢出问题
方案一:Jackson注解控制序列化(无需DTO)
使用@JsonIdentityInfo,它会在第一次序列化对象时输出完整内容,后续循环引用时用对象ID代替,既避免栈溢出,又保留关联数据:
在Person和Address类上都添加该注解:
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
示例(Person类):
@Entity @Getter @Setter @RequiredArgsConstructor @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") public class Person implements Serializable { // ... 其他代码 }
方案二:DTO+MapStruct正确映射(推荐)
之前DTO映射后personDTO为null,是因为MapStruct默认不会自动处理关联对象映射,需显式配置:
修正MapStruct映射器
让AddressMapper依赖PersonMapper,并指定关联字段映射规则:
@Mapper(uses = PersonMapper.class) // 引入PersonMapper处理关联对象 public interface AddressMapper { AddressMapper INSTANCE = Mappers.getMapper(AddressMapper.class); Address addressDtoToAddress(AddressDTO addressDTO); @Mapping(source = "person", target = "personDTO") // 显式指定Person到personDTO的映射 AddressDTO addressToAddressDto(Address address); }
确保JPA查询加载关联对象
修改AddressRepository,用JOIN FETCH强制加载关联的Person,避免懒加载导致null:
public interface AddressRepository extends JpaRepository<Address, Integer> { @Query("SELECT a FROM Address a JOIN FETCH a.person WHERE a.id = :id") Optional<Address> findByIdWithPerson(Integer id); }
同步修改AddressServices的查询方法:
public Optional<Address> getAddress(Integer id){ return addressRepository.findByIdWithPerson(id); }
Controller返回DTO
在接口中转换为DTO后返回,彻底避免实体类序列化循环:
@GetMapping("/get-address") public ResponseEntity<AddressDTO> getAddress(@RequestParam Integer id) { return addressServices.getAddress(id) .map(address -> ResponseEntity.ok(AddressMapper.INSTANCE.addressToAddressDto(address))) .orElse(ResponseEntity.notFound().build()); }
3. 修正数据保存逻辑
PersonService中属性名错误(实体类是personName,代码中用了userName),修正后确保关联关系正确:
public String createUser(){ Person u1 = new Person(); u1.setPersonName("test"); u1.setPersonLastName("test"); Address address = new Address("test", "test", "test", u1); u1.setAddress(address); personRepository.save(u1); return "Save is successful"; }
内容的提问来源于stack exchange,提问作者smhylc
相关产品推荐
相关产品推荐

