You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让Spring-data-mongo忽略List<Entity>中的空@DBRef?

解决Spring Data Mongo中List忽略缺失引用的问题

要实现Morphia中@Reference(ignoreMissing = true)的效果——自动跳过列表中无法加载的DBRef、避免出现null元素,需要同时处理批量DBRef获取和单个DBRef读取两种场景,并且在集合转换阶段过滤null实体。以下是具体实现方案:


1. 自定义DbRefResolver,覆盖核心读取方法

Spring Data Mongo在处理单元素DBRef列表时会调用readRef,多元素时调用bulkFetch,因此需要同时重写这两个方法,确保缺失的引用返回null或被过滤:

import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
import org.springframework.data.mongodb.core.convert.MongoDatabaseFactory;
import org.springframework.lang.NonNull;
import org.bson.Document;
import com.mongodb.DBRef;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

public class IgnoreMissingDbRefResolver extends DefaultDbRefResolver {

    public IgnoreMissingDbRefResolver(MongoDatabaseFactory mongoDbFactory) {
        super(mongoDbFactory);
    }

    // 批量获取DBRef时,过滤掉不存在的文档
    @Override
    @NonNull
    public List<Document> bulkFetch(@NonNull List<DBRef> refs) {
        return super.bulkFetch(refs)
                .stream()
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }

    // 读取单个DBRef时,找不到文档返回null(后续集合转换时过滤)
    @Override
    public Document readRef(@NonNull DBRef ref) {
        try {
            return super.readRef(ref);
        } catch (Exception e) {
            // 捕获文档不存在的异常,返回null
            return null;
        }
    }
}

2. 自定义MappingMongoConverter,过滤集合中的null实体

默认情况下,Spring会将null文档转换为null实体并保留在列表中,因此需要重写集合读取逻辑,自动移除null元素:

import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.lang.NonNull;
import org.bson.Document;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

public class IgnoreNullElementsMappingConverter extends MappingMongoConverter {

    public IgnoreNullElementsMappingConverter(IgnoreMissingDbRefResolver dbRefResolver, MongoMappingContext mappingContext) {
        super(dbRefResolver, mappingContext);
    }

    @Override
    protected <T> List<T> readCollection(@NonNull Class<T> targetType, @NonNull List<Document> source) {
        return super.readCollection(targetType, source)
                .stream()
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }
}

3. 配置Bean替换默认组件

在Spring Boot配置类中注册自定义的解析器和转换器,让全局生效:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.convert.SimpleMongoClientDatabaseFactory;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import com.mongodb.client.MongoClient;

@Configuration
public class MongoConfig {

    @Bean
    public MongoConverter mongoConverter(MongoClient mongoClient, MongoMappingContext mappingContext) {
        // 替换为你的数据库名称
        SimpleMongoClientDatabaseFactory dbFactory = new SimpleMongoClientDatabaseFactory(mongoClient, "your-database-name");
        IgnoreMissingDbRefResolver dbRefResolver = new IgnoreMissingDbRefResolver(dbFactory);
        
        IgnoreNullElementsMappingConverter converter = new IgnoreNullElementsMappingConverter(dbRefResolver, mappingContext);
        converter.setTypeMapper(getDefaultMongoTypeMapper());
        
        return converter;
    }

    @Bean
    public MongoTemplate mongoTemplate(MongoClient mongoClient, MongoConverter mongoConverter) {
        MongoTemplate template = new MongoTemplate(mongoClient, "your-database-name");
        template.setConverter(mongoConverter);
        return template;
    }
}

局部场景替代方案

如果不需要全局生效,也可以在实体的setter方法中手动过滤null元素:

public class Book {
    @DBRef
    private List<Author> authors;

    public void setAuthors(List<Author> authors) {
        this.authors = authors != null 
                ? authors.stream().filter(Objects::nonNull).collect(Collectors.toList()) 
                : null;
    }
}

这种方式无需修改全局配置,但需要为每个涉及的实体添加过滤逻辑。


内容的提问来源于stack exchange,提问作者takanuva15

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 22:33:23