Données de printemps MongoDb: mappingmongoconverter supprimer la classe

La valeur par défaut MappingMongoConverter ajoute une clé de type personnalisée ("_class") à chaque objet de la base de données. Donc, si je crée une personne:

package my.dto;
public class Person {
    String name;
    public Person(String name) {
        this.name = name; 
    }
}

Et enregistrez - le dans la base de données:

MongoOperations ops = new MongoTemplate(new Mongo(), "users");
ops.insert(new Person("Joe"));

L'objet résultant dans le mongo sera:

{ "_id" : ObjectId("4e2ca049744e664eba9d1e11"), "_class" : "my.dto.Person", "name" : "Joe" }

Questions:

  1. Quelles sont les implications du déplacement de la classe Person dans un espace de noms différent?

  2. Est-il possible de ne pas polluer l'objet avec la clé "_class"; sans écrire un convertisseur unique juste pour la classe de personne?

28
demandé sur Oliver Gierke 2011-07-25 03:39:21

8 réponses

Voici donc l'histoire: nous ajoutons le type par défaut comme une sorte d'indice quelle classe instancier réellement. Comme vous devez diriger un type pour lire le document via MongoTemplate de toute façon, il y a deux options possibles:

  1. vous remettez un type auquel le type stocké réel peut être affecté. Dans ce cas, nous considérons le type stocké, utilisez-le pour la création d'objets. L'exemple classique ici est de faire des requêtes polymorphes. Supposons que vous ayez une classe abstraite Contact et votre Person. Vous pouvez alors interroger pour Contacts et nous avons essentiellement ont déterminer un type à instancier.
  2. Si vous - d'un autre côté-passez dans un type complètement différent, nous serions simplement marshal dans ce type donné, pas dans celui stocké dans le document en fait. Cela couvrirait votre question ce qui se passe si vous déplacez le type.

Vous pourriez être intéressé à regarder Ce ticket qui couvre une sorte de stratégie de mappage de type enfichable pour transformer les informations de type en une réelle type. Cela peut servir simplement à économiser de l'espace car vous voudrez peut-être réduire un nom de classe qualifié à un hachage de quelques lettres. Cela permettrait également des scénarios de migration plus complexes où vous pourriez trouver des clés de type complètement arbitraires produites par un autre client de magasin de données et les lier à des types Java.

21
répondu Oliver Gierke 2011-07-25 19:08:39

Voici mon annotation, et ça marche.

@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;

    }

}
15
répondu mkyong 2012-09-12 13:54:53
<mongo:mongo host="hostname" port="27017">
<mongo:options
...options...
</mongo:mongo>
<mongo:db-factory dbname="databasename" username="user" password="pass"                     mongo-ref="mongo"/>
<bean id="mongoTypeMapper"     class="org.springframework.data.mongodb.core.convert.DefaultMongoTypeMapper">
<constructor-arg name="typeKey"><null/></constructor-arg>
</bean>
<bean id="mongoMappingContext"      class="org.springframework.data.mongodb.core.mapping.MongoMappingContext" />
<bean id="mongoConverter"     class="org.springframework.data.mongodb.core.convert.MappingMongoConverter">
<constructor-arg name="mongoDbFactory" ref="mongoDbFactory" />
<constructor-arg name="mappingContext" ref="mongoMappingContext" />
<property name="typeMapper" ref="mongoTypeMapper"></property>
</bean>
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg name="mongoDbFactory" ref="mongoDbFactory"/>
<constructor-arg name="mongoConverter" ref="mongoConverter" />
<property name="writeResultChecking" value="EXCEPTION" /> 
</bean>
5
répondu AKKI 2013-06-15 06:07:09

Si vous voulez désactiver l'attribut _class par défaut, mais conserver le polymorfisme pour les classes spécifiées, vous pouvez définir explicitement le type de Champ _class (facultatif) en configurant:

@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;
}

Cela préservera le champ _class (ou tout ce que vous voulez nommer dans construtor) pour Uniquement les entités spécifiées.

Vous pouvez également écrire propre TypeInformationMapper par exemple en fonction des annotations. Si vous annotez votre document par @DocumentType("aliasName"), vous conserverez le polymorphisme en gardant l'alias de la classe.

Oui expliqué brièvement sur mon blog , Mais voici un morceau de code rapide: https://gist.github.com/athlan/6497c74cc515131e1336

4
répondu Athlan 2015-09-22 19:41:28

Bien que la réponse de Mkyong fonctionne toujours, je voudrais ajouter ma version de solution car quelques bits sont obsolètes et peuvent être au bord du nettoyage.

Par exemple : MappingMongoConverter(mongoDbFactory(), new MongoMappingContext()) est dépréciée en faveur de new MappingMongoConverter(dbRefResolver, new MongoMappingContext()); et SimpleMongoDbFactory(new Mongo(), "databasename"); en faveur de new SimpleMongoDbFactory(new MongoClient(), database);.

Donc, ma dernière réponse de travail sans avertissements de dépréciation est:

@Configuration
public class SpringMongoConfig {

    @Value("${spring.data.mongodb.database}")
    private String database;

    @Autowired
    private MongoDbFactory mongoDbFactory;

    public @Bean MongoDbFactory mongoDBFactory() throws Exception {
        return new SimpleMongoDbFactory(new MongoClient(), database);
    }

    public @Bean MongoTemplate mongoTemplate() throws Exception {

        DbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoDbFactory);

        // Remove _class
        MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, new MongoMappingContext());
        converter.setTypeMapper(new DefaultMongoTypeMapper(null));

        return new MongoTemplate(mongoDBFactory(), converter);

    }

}

J'espère que cela aidera les personnes qui voudraient avoir une classe propre sans avertissement de dépréciation.

3
répondu harshavmb 2017-06-11 05:05:29

C'est ma solution d'une ligne:

@Bean 
public MongoTemplate mongoTemplateFraud() throws UnknownHostException {

  MongoTemplate mongoTemplate = new MongoTemplate(getMongoClient(), dbName);
  ((MappingMongoConverter)mongoTemplate.getConverter()).setTypeMapper(new DefaultMongoTypeMapper(null));//removes _class
  return mongoTemplate;
}
2
répondu kozla13 2016-09-02 14:37:01

J'ai lutté longtemps avec ce problème. J'ai suivi l'approche de mkyong mais quand j'ai introduit un attribut LocalDate (toute classe Jsr310 de Java 8) j'ai reçu l'exception suivante:

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

Le convertisseur correspondant org.springframework.format.datetime.standard.DateTimeConverters fait partie de Spring 4.1 et est référencé dans Spring Data MongoDB 1.7. Même si j'ai utilisé des versions plus récentes, le convertisseur n'a pas sauté.

La solution était d'utiliser le MappingMongoConverter existant et de ne fournir qu'un nouveau DefaultMongoTypeMapper (le code de mkyong est sous commentaire):

@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)
    }

Pour résumer:

  • étendre AbstractMongoConfiguration
  • annoter avec EnableMongoRepositories
  • dans mongoTemplate get converter from base class, cela garantit que les classes de conversion de type sont enregistrées
1
répondu ChrLipp 2017-09-18 07:51:51

Il vous suffit d'ajouter l'annotation @ TypeAlias à la définition de classe en changeant le mappeur de type

0
répondu Matt 2018-05-10 11:22:08