Spring data MongoDb: MappingMongoConverter remove _class

后端 未结 10 916
迷失自我
迷失自我 2020-12-04 21:48

The default MappingMongoConverter adds a custom type key (\"_class\") to each object in the database. So, if I create a Person:

package my.d         


        
相关标签:
10条回答
  • 2020-12-04 22:05

    I struggled a long time with this problem. I followed the approach from mkyong but when I introduced a LocalDate attribute (any JSR310 class from Java 8) I received the following exception:

    org.springframework.core.convert.ConverterNotFoundException:
    No converter found capable of converting from type [java.time.LocalDate] to type [java.util.Date]
    

    The corresponding converter org.springframework.format.datetime.standard.DateTimeConverters is part of Spring 4.1 and is referenced in Spring Data MongoDB 1.7. Even if I used newer versions the converter didn't jump in.

    The solution was to use the existing MappingMongoConverter and only provide a new DefaultMongoTypeMapper (the code from mkyong is under comment):

    @Configuration
    @EnableMongoRepositories
    class BatchInfrastructureConfig extends AbstractMongoConfiguration
    {
        @Override
        protected String getDatabaseName() {
            return "yourdb"
        }
    
        @Override
        Mongo mongo() throws Exception {
            new Mongo()
        }
    
        @Bean MongoTemplate mongoTemplate()
        {
            // overwrite type mapper to get rid of the _class column
    //      get the converter from the base class instead of creating it
    //      def converter = new MappingMongoConverter(mongoDbFactory(), new MongoMappingContext())
            def converter = mappingMongoConverter()
            converter.typeMapper = new DefaultMongoTypeMapper(null)
    
            // create & return template
            new MongoTemplate(mongoDbFactory(), converter)
        }
    

    To summarize:

    • extend AbstractMongoConfiguration
    • annotate with EnableMongoRepositories
    • in mongoTemplate get converter from base class, this ensures that the type conversion classes are registered
    0 讨论(0)
  • 2020-12-04 22:06

    For Spring Boot 2.3.0.RELEASE it's more easy, just override the method mongoTemplate, it's already has all things you need to set type mapper. See the following example:

    @Configuration
    @EnableMongoRepositories(
    // your package ...
    )
    public class MongoConfig extends AbstractMongoClientConfiguration {
    
        // .....
    
        @Override
        public MongoTemplate mongoTemplate(MongoDatabaseFactory databaseFactory, MappingMongoConverter converter) {
            // remove __class field from mongo
            converter.setTypeMapper(new DefaultMongoTypeMapper(null));
            return super.mongoTemplate(databaseFactory, converter);
        }
    
        // .....
    
    }
    
    0 讨论(0)
  • 2020-12-04 22:07
    @Configuration
    public class MongoConfig {
    
        @Value("${spring.data.mongodb.database}")
        private String database;
    
        @Value("${spring.data.mongodb.host}")
        private String host;
    
        public @Bean MongoDbFactory mongoDbFactory() throws Exception {
            return new SimpleMongoDbFactory(new MongoClient(host), database);
        }
    
        public @Bean MongoTemplate mongoTemplate() throws Exception {
    
            MappingMongoConverter converter = new MappingMongoConverter(new DefaultDbRefResolver(mongoDbFactory()),
                    new MongoMappingContext());
            converter.setTypeMapper(new DefaultMongoTypeMapper(null));
    
            MongoTemplate mongoTemplate = new MongoTemplate(mongoDbFactory(), converter);
    
            return mongoTemplate;
    
        }
    
    }
    
    0 讨论(0)
  • 2020-12-04 22:14

    Here's my annotation, and it works.

    @Configuration
    public class AppMongoConfig {
    
        public @Bean
        MongoDbFactory mongoDbFactory() throws Exception {
            return new SimpleMongoDbFactory(new Mongo(), "databasename");
        }
    
        public @Bean
        MongoTemplate mongoTemplate() throws Exception {
    
            //remove _class
            MappingMongoConverter converter = new MappingMongoConverter(mongoDbFactory(), new MongoMappingContext());
            converter.setTypeMapper(new DefaultMongoTypeMapper(null));
    
            MongoTemplate mongoTemplate = new MongoTemplate(mongoDbFactory(), converter);
    
            return mongoTemplate;
    
        }
    
    }
    
    0 讨论(0)
  • 2020-12-04 22:22

    So here's the story: we add the type by default as some kind of hint what class to instantiate actually. As you have to pipe in a type to read the document into via MongoTemplate anyway there are two possible options:

    1. You hand in a type the actual stored type can be assigned to. In that case we consider the stored type, use that for object creation. Classical example here is doing polymorphic queries. Suppose you have an abstract class Contact and your Person. You could then query for Contacts and we essentially have to determine a type to instantiate.
    2. If you - on the other hand - pass in a completely different type we'd simply marshal into that given type, not into the one stored in the document actually. That would cover your question what happens if you move the type.

    You might be interested in watching this ticket which covers some kind of pluggable type mapping strategy to turn the type information into an actual type. This can serve simply space saving purposes as you might want to reduce a long qualified class name to a hash of a few letters. It would also allow more complex migration scenarios where you might find completely arbitrary type keys produced by another datastore client and bind those to Java types.

    0 讨论(0)
  • 2020-12-04 22:27

    If you want to disable _class attribute by default, but preserve polymorfism for specified classes, you can explictly define the type of _class (optional) field by configuing:

    @Bean
    public MongoTemplate mongoTemplate() throws Exception {
        Map<Class<?>, String> typeMapperMap = new HashMap<>();
        typeMapperMap.put(com.acme.domain.SomeDocument.class, "role");
    
        TypeInformationMapper typeMapper1 = new ConfigurableTypeInformationMapper(typeMapperMap);
    
        MongoTypeMapper typeMapper = new DefaultMongoTypeMapper(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY, Arrays.asList(typeMapper1));
        MappingMongoConverter converter = new MappingMongoConverter(mongoDbFactory(), new MongoMappingContext());
        converter.setTypeMapper(typeMapper);
    
        MongoTemplate mongoTemplate = new MongoTemplate(mongoDbFactory(), converter);
        return mongoTemplate;
    }
    

    This will preserve _class field (or whatever you want to name in construtor) for only specified entities.

    You can also write own TypeInformationMapper for example based on annotations. If you annotate your document by @DocumentType("aliasName") you will keep polymorphism by keeping alias of class.

    I have explained briefly it on my blog, but here is some piece of quick code: https://gist.github.com/athlan/6497c74cc515131e1336

    0 讨论(0)
提交回复
热议问题