web-dev-qa-db-ger.com

Frühlingsdaten MongoDb: MappingMongoConverter _class entfernen

Der Standardwert MappingMongoConverter fügt jedem Objekt in der Datenbank einen benutzerdefinierten Typschlüssel ("_class") hinzu. Also, wenn ich eine Person erstelle:

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

und speichere es in der Datenbank:

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

das resultierende Objekt im Mongo wird sein:

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

Fragen:

  1. Was bedeutet es, die Person-Klasse in einen anderen Namespace zu verschieben?

  2. Ist es möglich, das Objekt nicht mit der Taste "_class" zu verschmutzen? ohne einen eindeutigen Konverter nur für die Person-Klasse zu schreiben?

29
Yuriy Nemtsov

Hier ist die Geschichte: Wir fügen den Typ standardmäßig als Hinweis hinzu, welche Klasse tatsächlich instanziiert werden soll. Da Sie einen Typ eingeben müssen, um das Dokument über MongoTemplate einzulesen, gibt es zwei Möglichkeiten:

  1. Sie geben einen Typ ein, dem der aktuell gespeicherte Typ zugeordnet werden kann. In diesem Fall berücksichtigen wir den gespeicherten Typ und verwenden ihn für die Objekterstellung. Ein klassisches Beispiel hierfür sind polymorphe Abfragen. Angenommen, Sie haben eine abstrakte Klasse Contact und Ihre Person. Sie könnten dann nach Contacts fragen, und wir müssen im Wesentlichen einen zu instanziierenden Typ bestimmen .
  2. Wenn Sie andererseits einen völlig anderen Typ eingeben, werden wir einfach den angegebenen Typ eingeben, nicht den im Dokument tatsächlich gespeicherten. Das würde deine Frage abdecken, was passiert, wenn du den Typ verschiebst.

Sie könnten daran interessiert sein, dieses Ticket anzusehen, das eine Art steckbarer Typzuordnungsstrategie behandelt, um die Typinformationen in einen tatsächlichen Typ umzuwandeln. Dies kann einfach zu platzsparenden Zwecken dienen, da Sie einen langen qualifizierten Klassennamen möglicherweise auf einen Hash von wenigen Buchstaben reduzieren möchten. Dies würde auch komplexere Migrationsszenarien ermöglichen, in denen Sie möglicherweise vollständig beliebige Typschlüssel finden, die von einem anderen Datenspeicher-Client erstellt wurden, und diese an Java -Typen binden.

27
Oliver Drotbohm

Hier ist meine Anmerkung, und es funktioniert.

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

    }

}
17
mkyong
<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
AKKI

Wenn Sie das _class-Attribut standardmäßig deaktivieren möchten, aber den Polymorfismus für bestimmte Klassen beibehalten möchten, können Sie den Typ des _class-Felds (optional) explizit definieren, indem Sie Folgendes konfigurieren:

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

Dadurch wird das Feld _class (oder was auch immer Sie in construtor benennen möchten) nur für angegebene Entitäten beibehalten.

Sie können auch eigene TypeInformationMapper schreiben, zum Beispiel basierend auf Anmerkungen. Wenn Sie Ihr Dokument durch @DocumentType("aliasName") mit Anmerkungen versehen, behalten Sie den Polymorphismus bei, indem Sie den Alias ​​der Klasse beibehalten.

Ich habe es in meinem Blog kurz erklärt , aber hier ist ein kurzer Code: https://Gist.github.com/athlan/6497c74cc515131e1336

4
Athlan

Während Mkyongs Antwort noch funktioniert, möchte ich meine Lösungsversion hinzufügen, da nur wenige Bits veraltet sind und möglicherweise im Begriff sind, aufgeräumt zu werden.

Beispiel: MappingMongoConverter(mongoDbFactory(), new MongoMappingContext()) wird zugunsten von new MappingMongoConverter(dbRefResolver, new MongoMappingContext()); und SimpleMongoDbFactory(new Mongo(), "databasename"); zugunsten von new SimpleMongoDbFactory(new MongoClient(), database); nicht mehr empfohlen.

Meine letzte funktionierende Antwort ohne Verwerfungswarnungen lautet also:

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

    }

}

Hoffe, dies hilft Menschen, die eine saubere Klasse ohne Verwerfungswarnungen haben möchten.

3
harshavmb

Dies ist meine einzeilige Lösung:

@Bean 
public MongoTemplate mongoTemplateFraud() throws UnknownHostException {

  MongoTemplate mongoTemplate = new MongoTemplate(getMongoClient(), dbName);
  ((MappingMongoConverter)mongoTemplate.getConverter()).setTypeMapper(new DefaultMongoTypeMapper(null));//removes _class
  return mongoTemplate;
}
2
kozla13

Ich hatte lange mit diesem Problem zu kämpfen. Ich folgte dem Ansatz von mkyong , aber als ich ein LocalDate-Attribut einführte (eine beliebige JSR310-Klasse aus Java 8), erhielt ich die folgende Ausnahme:

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

Der entsprechende Konverter org.springframework.format.datetime.standard.DateTimeConverters ist Teil von Spring 4.1 und wird in Spring Data MongoDB 1.7 referenziert. Auch wenn ich neuere Versionen verwendet habe, ist der Konverter nicht eingesprungen.

Die Lösung bestand darin, die vorhandene MappingMongoConverter zu verwenden und nur eine neue DefaultMongoTypeMapper bereitzustellen (der Code von mkyong wird kommentiert):

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

Zusammenfassen:

  • erweitere AbstractMongoConfiguration
  • mit EnableMongoRepositories kommentieren
  • dies stellt in mongoTemplate get converter from base class sicher, dass die Typkonvertierungsklassen registriert sind
1
ChrLipp

sie müssen lediglich die Annotation @TypeAlias ​​zur Klassendefinition hinzufügen, um den Typ-Mapper zu ändern

0
Matt
@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