Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
188 views
in Technique[技术] by (71.8m points)

java - Problem with reading eclipse collection MutableMap from mongodb using spring boot and reactive mongo template

I have a POJO which has an ec MutableMap as an attribute which I am trying to save to mongodb and then read back in a separate method. I can save the POJO to mongodb, but when I try to read it back the call fails saying that the MutableMap is an unsupported Map interface. Some options include: 1. Change the MutableMap to java.util.Map in the POJO. 2. Write a Read Converter to map from Document to POJO. 3. Write or use a CodecProvider to help with the conversion. When the POJO is written to mongodb, the _class for MutableMap is set, so I would think spring boot can convert it back to the POJO with minimal effort. Any other options here? My preference would be to use a CodecProvider. I have written a Converter, add it into MongoCustomConversions and I was able to get the code to execute fine, but do not want to do that for each POJO which uses MutablMap. Any help on getting this to work would be appreciated.

AppConfig

package ibkr.algo.liveshell;

import static org.bson.codecs.configuration.CodecRegistries.fromProviders;
import static org.bson.codecs.configuration.CodecRegistries.fromRegistries;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.bson.Document;
import org.bson.codecs.Codec;
import org.bson.codecs.configuration.CodecProvider;
import org.bson.codecs.configuration.CodecRegistries;
import org.bson.codecs.configuration.CodecRegistry;
import org.bson.codecs.pojo.ClassModel;
import org.bson.codecs.pojo.PojoCodecProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Primary;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.mongodb.CodecRegistryProvider;
import org.springframework.data.mongodb.ReactiveMongoDatabaseFactory;
import org.springframework.data.mongodb.config.AbstractReactiveMongoConfiguration;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
import org.springframework.data.mongodb.core.SimpleReactiveMongoDatabaseFactory;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.convert.MongoCustomConversions;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.datatype.eclipsecollections.EclipseCollectionsModule;
import com.fasterxml.jackson.datatype.guava.GuavaModule;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mongodb.MongoClientSettings;
import com.mongodb.reactivestreams.client.*;

import ibkr.algo.liveshell.converters.DailyCandleAggsReadConverter;
import ibkr.algo.liveshell.pojos.candle.DailyCandleAggs;
import ibkr.algo.liveshell.util.DateTimeHelper;

@Configuration
public class AppConfigAggregationsReactiveMongo extends AbstractReactiveMongoConfiguration {

    private final List<Converter<?, ?>> converters = new ArrayList<Converter<?, ?>>();
    
    @Autowired
    private MappingMongoConverter mongoConverter;
    
    @Override
    public com.mongodb.reactivestreams.client.MongoClient reactiveMongoClient() {
        // TODO Auto-generated method stub
            
        MongoClientSettings settings = MongoClientSettings.builder()
                .codecRegistry(pojoRegistry())
//              .codecRegistry(jacksonCodecRegistry())
                .build();
        
        MongoClient mClient = MongoClients.create(settings);
                
        return mClient;
    }
            
    public @Bean CodecRegistry pojoRegistry() {

        // Create a CodecRegistry containing the PojoCodecProvider instance.
        CodecProvider pojoCodecProvider = PojoCodecProvider.builder().register("ibkr.algo.liveshell.pogos")
                .register("org.eclipse.collections")
                .build();

        CodecRegistry codecReg = CodecRegistries.fromRegistries(MongoClients.getDefaultCodecRegistry(),
                fromProviders(PojoCodecProvider.builder().automatic(true).build()));
        
        return codecReg;

    }
    @Qualifier("candleAggsMongoTemplate")
    public @Bean ReactiveMongoTemplate candleAggsMongoTemplate() {
        
        
        //CodecRegistryProvider codecRegistryProvider = CodecRegistries.
        mongoConverter.setCodecRegistryProvider(mongoDatabaseFactory());
        mongoConverter.setCustomConversions(CustomConverters());
        mongoConverter.afterPropertiesSet();
        
        return new ReactiveMongoTemplate(mongoDatabaseFactory() , mongoConverter);
    }
    
    @Override
    protected String getDatabaseName() {
        // TODO Auto-generated method stub
        return "Aggregations";
    }
    
    @Primary
    public @Bean SimpleReactiveMongoDatabaseFactory mongoDatabaseFactory() {
        
        SimpleReactiveMongoDatabaseFactory fact = new SimpleReactiveMongoDatabaseFactory(reactiveMongoClient(), getDatabaseName());
        
        return fact;
        
    }
    
    public @Bean ObjectMapper mapp() {
        
        ObjectMapper mapper = new ObjectMapper();
        
        mapper = JsonMapper.builder()
                .addModule(new EclipseCollectionsModule())
                .addModule(new GuavaModule())
                .build();
        
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
            .configure(MapperFeature.DEFAULT_VIEW_INCLUSION, true)
            .configure(SerializationFeature.INDENT_OUTPUT, true);
        
        return mapper;
    }
    
    protected MongoCustomConversions CustomConverters() {

        // using the custom converter and jackson mapper can return back the object without throwing exception
        // comment out and throws unsupported map interface in spring boot
        // do not want to write converters for each so going to CodecRegistry
        //converters.add(new DailyCandleAggsReadConverter(mapp()));
        return new MongoCustomConversions(converters);
    }
    
    public AppConfigAggregationsReactiveMongo() {
        // TODO Auto-generated constructor stub
    }
}

ReactiiveTest

package algo.livedata;

import org.assertj.core.util.Arrays;
import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ContextConfiguration;

import ibkr.algo.liveshell.AppConfigAggregationsReactiveMongo;
import ibkr.algo.liveshell.AppConfigReactiveMongo;
import ibkr.algo.liveshell.interfaces.ICandleAggsDataService;
import ibkr.algo.liveshell.interfaces.ICandleAggsTest;
import ibkr.algo.liveshell.interfaces.IContractReactiveDataService;
import ibkr.algo.liveshell.interfaces.ILiveRequestClientService;
import ibkr.algo.liveshell.pojos.candle.BarAggs;
import ibkr.algo.liveshell.pojos.candle.DailyCandleAggs;
import ibkr.algo.liveshell.pojos.candle.DailyCandleAggsTest;
import ibkr.algo.liveshell.pojos.contract.*;
import ibkr.algo.liveshell.pojos.market.StockWatchlist;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;

@SpringBootTest
@ContextConfiguration(classes = {AppConfigAggregationsReactiveMongo.class,TestConfig.class})
public class ReactiveTests {

    @Autowired
    private ICandleAggsDataService candleAggSvc;
    
    @Autowired
    private ICandleAggsTest candleAggsTest;
        
    @Test
    public void contextLoads() {
    }
    //@Test
    public void TestMongoCodec() {
        
    }
    @Test
    public void TestCandleAggsSave() {
        
        BarAggs agg = new BarAggs(1612353600, 15.15, 15.17, 15.15, 15.17, 200, 2, 1612351800, 1612352095, 4, false, 1612351800, 1612351980);
        
        DailyCandleAggsTest dailyAggs = new DailyCandleAggsTest();
        
        dailyAggs.set_reqId(1);
        dailyAggs.setLastTradeDate(20210203);
        dailyAggs.AddToMap(agg);
        
        Mono<DailyCandleAggsTest> test1 = candleAggsTest.saveDailyCandleAggs(dailyAggs)
            .doOnSuccess(onSuccess -> System.out.println(onSuccess))
            .doOnError(onError -> System.out.println(onError))
            .log();

        StepVerifier
        .create(test1)
        .expectNext(dailyAggs)
        .expectComplete()
        .verify();
        
    }
    @Test
    public void TestCandleAggsRead() {
        Mono<DailyCandleAggsTest> test2 = candleAggsTest.getPrevTradingDayAgg(20210203)
                .doOnNext(onNext -> System.out.println(onNext.get_id()))
                .doOnError(onError -> System.out.println(onError))
                .log();

        StepVerifier
        .create(test2)
        .expectNextCount(1)
        .expectComplete()
        .verify();
        
    }
}

POJO

package ibkr.algo.liveshell.pojos.candle;

import org.eclipse.collections.api.multimap.list.MutableListMultimap;
import org.eclipse.collections.impl.multimap.list.FastListMultimap;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

@Document(collection="DailyCandleAggsTest")
public class DailyCandleAggsTest {

    @Id
    private String _id;
    
    private int _reqId;
        
    private long lastTradeDate;

    private MutableListMultimap<Integer, BarAggs> _candleDailyAggs = FastListMultimap.newMultimap();

    public String get_id() {
        return _id;
    }

    public void set_id(String _id) {
        this._id = _id;
    }

    public int get_reqId() {
        return _reqId;
    }

    public void set_reqId(int _reqId) {
        this._reqId = _reqId;
    }

    public MutableListMultimap<Integer, BarAggs> get_candleDailyAggs() {
        return _candleDailyAggs;
    }

    public void set_candleDailyAggs(MutableListMultimap<Integer, BarAggs> _candleDailyAggs) {
        this._candleDailyAggs = _candleDailyAggs;
    }

    public long getLastTradeDate() {
        return lastTradeDate;
    }

    public void setLastTradeDate(long lastTradeDate) {
        this.lastTradeDate = lastTradeDate;
    }
    
    public void AddToMap(BarAggs agg) {
        _candleDailyAggs.put(1, agg);
    }
    
    public DailyCandleAggsTest() {
        
    }
}

Service Class

package ibkr.algo.liveshell.service;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;

import ibkr.algo.liveshell.interfaces.ICandleAggsTest;
import ibkr.algo.liveshell.pojos.candle.DailyCandleAggs;
import ibkr.algo.liveshell.pojos.candle.DailyCandleAggsTest;
import reactor.core.publisher.Mono;

@Service
public class CandleAggsTest implements ICan

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)
Waitting for answers

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...