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
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…