Spring Data MongoDB无法获取二级嵌套对象问题求助
问题场景
实体类采用二级@DocumentReference关联:
@Data @Builder @Document(collection = "emitted") @JsonInclude(JsonInclude.Include.NON_NULL) public class Emitter{ @Id private String id; @Field("installation") @DocumentReference(lazy = true) private Installation installation; // 其他字段 }
@Data @Builder @Document(collection = "installation") @JsonInclude(JsonInclude.Include.NON_NULL) public class Installation { @Id private String id; @Field("subject") @DocumentReference(lazy = true) private Subject subject; // 其他字段 }
@Data @Builder @Document(collection = "subject") @JsonInclude(JsonInclude.Include.NON_NULL) public class Subject { @Id private String id; // 其他字段 }
使用MapStruct处理嵌套对象映射避免循环引用,配置如下:
@ObjectFactory public <T> T map(@NonNull final String id, @TargetType Class<T> type) { return mongoTemplate.findById(id, type); }
现象:一级嵌套的Installation能正常获取,二级嵌套的Subject转换时抛出异常:
Caused by: org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [org.bson.types.ObjectId] to type [com.package.collections.Subject]
...
at org.springframework.data.mongodb.core.MongoTemplate.findById(MongoTemplate.java:922)
at com.package.myapp.services.mapper.ReferenceMapper.map(ReferenceMapper.java:26)
at com.package.myapp.services.mapper.InstallationMapperImpl.toEntity(InstallationMapperImpl.java:102)
解决方案
1. 修改MapStruct的ObjectFactory方法参数类型
二级嵌套时,MongoDB传递的ID是ObjectId对象而非String,原方法仅接受String类型导致转换失败。调整方法兼容ObjectId:
@ObjectFactory public <T> T map(@NonNull final Object id, @TargetType Class<T> type) { String idStr = id instanceof ObjectId ? ((ObjectId) id).toHexString() : id.toString(); return mongoTemplate.findById(idStr, type); }
2. 新增ObjectId到实体类的类型转换器
为MapStruct添加专属转换器,处理ObjectId到Subject的转换:
@Converter public class ObjectIdToSubjectConverter implements Converter<ObjectId, Subject> { private final MongoTemplate mongoTemplate; public ObjectIdToSubjectConverter(MongoTemplate mongoTemplate) { this.mongoTemplate = mongoTemplate; } @Override public Subject convert(ObjectId source) { return mongoTemplate.findById(source.toHexString(), Subject.class); } }
在MapStruct的Mapper接口上指定使用该转换器:
@Mapper(componentModel = "spring", uses = {ObjectIdToSubjectConverter.class}) public interface InstallationMapper { // 映射方法定义 }
3. 触发懒加载初始化
由于使用了lazy=true,二级嵌套的Subject可能未被初始化。在MapStruct映射前手动触发加载:
// 在映射Installation前调用getSubject()触发懒加载 installation.getSubject();
关键原因
一级嵌套时,MapStruct传递的ID是String类型(Emitter中存储的Installation ID已被转换为String);二级嵌套时,Installation中存储的Subject ID是未转换的ObjectId对象,原MapStruct工厂方法无法处理该类型,导致转换器缺失异常。
内容的提问来源于stack exchange,提问作者Mario Biagioni

