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:
Quelles sont les implications du déplacement de la classe Person dans un espace de noms différent?
Est-il possible de ne pas polluer l'objet avec la clé "_class"; sans écrire un convertisseur unique juste pour la classe de personne?
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:
- 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 votrePerson
. Vous pouvez alors interroger pourContact
s et nous avons essentiellement ont déterminer un type à instancier. - 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.
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;
}
}
<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>
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
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.
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;
}
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
Il vous suffit d'ajouter l'annotation @ TypeAlias à la définition de classe en changeant le mappeur de type