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 sur 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" }
Des questions:
Quelles sont les implications du déplacement de la classe Person dans un autre espace de noms?
Est-il possible de ne pas polluer l'objet avec la clé "_class"; sans écrire un convertisseur unique juste pour la classe Personne?
Alors voici l'histoire: nous ajoutons le type par défaut comme une sorte d'indice sur la classe à instancier réellement. Comme vous devez diriger un type dans lequel lire le document via MongoTemplate
, deux options sont possibles:
Contact
et votre Person
. Vous pouvez alors interroger Contact
s et nous avons essentiellement devons déterminer le type à instancier.Vous voudrez peut-être regarder ce ticket qui couvre une sorte de stratégie de mappage de types enfichable pour transformer les informations de type en un type réel. Cela peut servir simplement à économiser de l'espace car vous pouvez réduire un nom de classe qualifié long à un hachage de quelques lettres. Cela permettrait également des scénarios de migration plus complexes dans lesquels vous pourriez trouver des clés de type complètement arbitraire produites par un autre client de banque 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 souhaitez désactiver l'attribut _class
par défaut tout en préservant le polymorphisme 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 ce que vous voulez nommer dans construtor) uniquement pour les entités spécifiées.
Vous pouvez également écrire votre propre variable TypeInformationMapper
, par exemple, en fonction d’annotations. Si vous annotez votre document avec @DocumentType("aliasName")
, vous conserverez le polymorphisme en conservant un alias de classe.
Je l'ai expliqué brièvement sur mon blog , mais voici un code rapide: https://Gist.github.com/athlan/6497c74cc515131e1336
Bien que la réponse de Mkyong fonctionne toujours, j'aimerais ajouter ma version de la solution car quelques bits sont obsolètes et risquent d'être sur le point d'être nettoyés.
Par exemple: MappingMongoConverter(mongoDbFactory(), new MongoMappingContext())
est déconseillé 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 la suivante:
@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 gens qui voudraient avoir une classe propre sans avertissements de dépréciation.
Ceci est ma solution en une seule 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 longtemps lutté avec ce problème. J'ai suivi l'approche de mkyong mais lorsque 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’utilisais des versions plus récentes, le convertisseur n’est pas intervenu.
La solution consistait à utiliser la MappingMongoConverter
existante et à ne fournir qu'une nouvelle DefaultMongoTypeMapper
(le code de mkyong est en 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)
}
Résumer:
AbstractMongoConfiguration
EnableMongoRepositories
mongoTemplate
obtenir un convertisseur de la classe de base, cela garantit que les classes de conversion de type sont enregistréesil vous suffit d'ajouter l'annotation @TypeAlias à la définition de la classe en changeant le mappeur de type