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

Spring Data MongoDB自定义转换器未生效引发类型转换异常

问题:MongoDB自定义ReadingConverter未生效,触发ClassCastException

场景还原

实体CommOutput定义属性:

private List<Map<String, Object>> calls;

自定义@ReadingConverter转换器:

public class ArrayListToMapConverter implements Converter<ArrayList, List<Map<String, Object>>> {
@Override
public List<Map<String, Object>> convert(ArrayList source) {
    System.out.println("**************il est dans convert***************");
    List<Map<String, Object>> resultList = new ArrayList<>();
    for (Object item : source) {
        if (item instanceof Map) {
            @SuppressWarnings("unchecked")
            Map<String, Object> mapItem = (Map<String, Object>) item;
            resultList.add(mapItem);
        }
    }
    return resultList;
}
}

创建自定义MongoConfig配置类注入带转换器的mongoTemplateCustom,查询时使用该模板,但业务层遍历calls时抛出异常:

java.lang.ClassCastException: class java.util.ArrayList cannot be cast to class java.util.Map

怀疑转换器未被调用。

问题根源分析

  1. 转换器类型匹配错误:当前转换器是将ArrayList转为List<Map<String,Object>>,但实际MongoDB返回的是List内的元素为ArrayList(而非Map),需要的是将单个ArrayList元素转为Map,而非整个集合转集合。
  2. 配置类冲突:存在两个继承MongoAutoConfiguration的配置类(MongoDaoTools和MongoConfig),导致Spring容器中Bean加载优先级混乱,自定义转换器的MongoCustomConversions未正确关联到mongoTemplateCustom的MappingMongoConverter。
  3. 字段投影影响:查询中使用include("calls.segmentId")这类嵌套字段投影,MongoDB返回的嵌套数组结构可能与自定义转换器的处理逻辑冲突。

解决步骤

1. 修正转换器逻辑

将转换器改为将单个ArrayList转为Map,Spring Data MongoDB会自动遍历集合为每个元素应用转换器:

@ReadingConverter
public class ArrayListToMapConverter implements Converter<ArrayList, Map<String, Object>> {
    @Override
    public Map<String, Object> convert(ArrayList source) {
        System.out.println("**************转换器已触发***************");
        Map<String, Object> resultMap = new HashMap<>();
        // 根据MongoDB中实际存储的ArrayList结构调整转换逻辑,示例为[key,value]成对结构
        for (int i = 0; i < source.size(); i += 2) {
            if (i+1 < source.size()) {
                resultMap.put(source.get(i).toString(), source.get(i+1));
            }
        }
        return resultMap;
    }
}

2. 清理配置类冲突

删除MongoDaoTools配置类,或合并两个配置类,调整MongoConfig确保Bean正确初始化:

@Configuration
public class MongoConfig extends MongoAutoConfiguration {
    private final DatabaseProperties properties;

    public MongoConfig(DatabaseProperties properties) {
        this.properties = properties;
    }

    @Bean
    @Primary
    public MongoClient mongoClient() {
        ConnectionString connectionString = new ConnectionString(properties.getDatabaseUri());
        return MongoClients.create(connectionString);
    }

    @Bean
    @Primary
    public MongoDatabaseFactory mongoDatabaseFactory(MongoClient client) {
        return new SimpleMongoClientDatabaseFactory(client, properties.getDatabase());
    }

    @Bean
    @Qualifier("mongoTemplateCustom")
    public MongoTemplate mongoTemplateCustom(MongoDatabaseFactory databaseFactory, MongoMappingContext mongoMappingContext) {
        DbRefResolver dbRefResolver = new DefaultDbRefResolver(databaseFactory);
        MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, mongoMappingContext);
        converter.setCustomConversions(customConversions());
        converter.setTypeMapper(new DefaultMongoTypeMapper(null));
        converter.afterPropertiesSet(); // 必须调用完成初始化
        return new MongoTemplate(databaseFactory, converter);
    }

    @Bean
    @Primary
    public MongoCustomConversions customConversions() {
        List<Converter<?, ?>> converters = new ArrayList<>();
        converters.add(new ArrayListToMapConverter());
        return new MongoCustomConversions(converters);
    }

    @Bean
    @Primary
    public MongoMappingContext mongoMappingContext() throws Exception {
        MongoMappingContext context = new MongoMappingContext();
        context.setInitialEntitySet(new HashSet<>(Collections.singleton(CommOutput.class))); // 指定映射实体
        context.afterPropertiesSet();
        return context;
    }
}

关键修改:

  • 注入MongoMappingContext作为Bean,确保上下文初始化完成
  • 调用converter.afterPropertiesSet()完成转换器注册
  • 指定映射实体类,避免自动扫描问题

3. 调整查询投影(可选)

若投影导致结构异常,先查询整个calls字段验证转换器是否生效:

// 修改fields部分,直接包含整个calls字段
query.fields().include("creationDate").include("_id").include("commStatus")
        .include("commId").include("personId").include("sendWay")
        .include("commForceTreatment").include("errors").include("sysSourceIds")
        .include("calls")
        .include("orderStatus").include("commVectorStatus")
        .include("distributorId");

4. 验证转换器触发

运行程序,查看控制台是否打印转换器内的日志,确认转换器已生效后,再根据实际数据调整转换逻辑。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 21:25:06