Spring ManyToOne关联实体Service测试时为null的问题解决
问题:测试时触发NullPointerException,因dormitoryEntity为null
异常信息
java.lang.NullPointerException: Cannot invoke "com.project.clickit.entity.DormitoryEntity.toDTO()" because "this.dormitoryEntity" is null
相关代码
DormitoryEntity实体类
@Setter @Getter @Builder @Table(name = "dormitory") @NoArgsConstructor //@AllArgsConstructor @Entity public class DormitoryEntity { @Id @Column(name = "dormitory_id") private String id; @Column(name = "dormitory_name") private String name; @Builder public DormitoryEntity(String id, String name) { this.id = id; this.name = name; } public DormitoryDTO toDTO() { return DormitoryDTO.builder() .id(this.id) .name(this.name) .build(); } }
FacilityEntity实体类
@Setter @Getter @Builder @Table(name = "facility") @NoArgsConstructor @AllArgsConstructor @Entity public class FacilityEntity { @Id @Column(name = "facility_id") private String id; @Column(name = "facility_name") private String name; @Column(name = "facility_info") private String info; @Column(name = "facility_open") private Integer open; @Column(name = "facility_close") private Integer close; @Column(name = "facility_img") private String img; @Column(name = "facility_terms") private String terms; @Column(name = "facility_extension_limit") private Integer extensionLimit; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "facility_dormitory") private DormitoryEntity dormitoryEntity; public FacilityEntity(String id, String name, String info, Integer open, Integer close, String img, String terms, Integer extensionLimit) { this.id = id; this.name = name; this.info = info; this.open = open; this.close = close; this.img = img; this.terms = terms; this.extensionLimit = extensionLimit; } public FacilityDTO toDTO() { return FacilityDTO.builder() .id(this.id) .name(this.name) .info(this.info) .open(this.open) .close(this.close) .img(this.img) .terms(this.terms) .extensionLimit(this.extensionLimit) .dormitoryDTO(this.dormitoryEntity.toDTO()) .build(); } }
FacilityRepository
@Query("SELECT new FacilityEntity(f.id, f.name, f.info, f.open, f.close, f.img, f.terms, f.extensionLimit, f.dormitoryEntity) FROM FacilityEntity f where f.id = :id") FacilityEntity findByFacilityId(@Param("id") String id);
FacilityService
@Transactional public FacilityDTO findById(String id) { return facilityRepository.findByFacilityId(id).toDTO(); }
测试代码
@Slf4j @ExtendWith({MockitoExtension.class}) public class FacilityServiceTest { @Mock private FacilityRepository facilityRepository; @InjectMocks private FacilityService facilityService; @Test void findByIdTest(){ given(facilityRepository.findByFacilityId("test_facility")).willReturn(new FacilityEntity()); FacilityDTO result = facilityService.findById("test_facility"); assertThat(result).isNotNull; } }
已尝试的解决方法
- 在@Test注解后添加
@Transactional(propagation = Propagation.SUPPORT)注解 - 将fetchType从
LAZY改为EAGER
补充说明
使用Postman发送Get请求时,FacilityService的findById方法运行正常,问题仅出现在使用Mock对象而非真实FacilityRepository的测试场景中。
解决方案
方式一:测试中构造完整的关联实体
测试里返回的new FacilityEntity()未初始化dormitoryEntity字段,导致toDTO()调用时触发空指针。需要构造包含关联宿舍实体的设施对象:
@Test void findByIdTest(){ // 构造关联的宿舍实体 DormitoryEntity dorm = DormitoryEntity.builder() .id("test_dorm") .name("测试宿舍") .build(); // 构造带关联的设施实体 FacilityEntity facility = FacilityEntity.builder() .id("test_facility") .name("测试设施") .dormitoryEntity(dorm) .build(); given(facilityRepository.findByFacilityId("test_facility")).willReturn(facility); FacilityDTO result = facilityService.findById("test_facility"); assertThat(result).isNotNull(); assertThat(result.getDormitoryDTO()).isNotNull(); }
方式二:在toDTO()方法中做空指针防护
修改FacilityEntity的toDTO()方法,处理关联对象为null的情况:
public FacilityDTO toDTO() { return FacilityDTO.builder() .id(this.id) .name(this.name) .info(this.info) .open(this.open) .close(this.close) .img(this.img) .terms(this.terms) .extensionLimit(this.extensionLimit) .dormitoryDTO(this.dormitoryEntity != null ? this.dormitoryEntity.toDTO() : null) .build(); }
额外优化建议
优先使用Builder模式创建实体对象,而非直接调用构造函数,避免遗漏关联字段或必填参数,减少类似空指针问题的发生。
内容的提问来源于stack exchange,提问作者TakeMeH0ME
相关产品推荐
相关产品推荐

