Spring data MongoDb: MappingMongoConverter удалить _class
По умолчанию MappingMongoConverter добавляет специальный тип ключа ( "_class" ) к каждому объекту в базе данных. Итак, если я создаю Person:
package my.dto;
public class Person {
String name;
public Person(String name) {
this.name = name;
}
}
и сохраните его в db:
MongoOperations ops = new MongoTemplate(new Mongo(), "users");
ops.insert(new Person("Joe"));
результирующий объект в монго будет:
{ "_id" : ObjectId("4e2ca049744e664eba9d1e11"), "_class" : "my.dto.Person", "name" : "Joe" }
Вопросы:
Ответы
Ответ 1
Итак, вот история: мы добавляем тип по умолчанию как какой-то намек на то, какой класс нужно создавать на самом деле. Поскольку вам нужно подключиться к типу, чтобы прочитать документ через MongoTemplate
, в любом случае есть два возможных варианта:
- Вы передаете тип, которому может быть присвоен фактический сохраненный тип. В этом случае мы рассмотрим сохраненный тип, используйте это для создания объекта. Классический пример здесь - полиморфные запросы. Предположим, что у вас есть абстрактный класс
Contact
и ваш Person
. Затем вы можете запросить Contact
, и мы по существу должны определить тип для создания экземпляра.
- Если вы, с другой стороны, передаете совершенно другой тип, мы просто будем маршалировать этот тип, а не тот, который хранится в документе. Это затронет ваш вопрос, что произойдет, если вы переместите тип.
Вам может быть интересно наблюдать этот билет, который охватывает какую-то стратегию сопоставления типа подключаемого типа, чтобы превратить информацию типа в фактический тип. Это может служить просто задачами экономии пространства, так как вам может потребоваться уменьшить длинное квалифицированное имя класса до хэша нескольких букв. Это также позволит более сложные сценарии миграции, где вы можете найти полностью произвольные ключи типа, созданные другим клиентом хранилища данных, и привязать их к типам Java.
Ответ 2
Здесь моя аннотация, и она работает.
@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;
}
}
Ответ 3
<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>
Ответ 4
Если вы хотите отключить атрибут _class
по умолчанию, но сохранить полиморфизм для указанных классов, вы можете явно определить тип поля _class
(необязательно), настроив:
@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;
}
Это сохранит поле _class
(или то, что вы хотите назвать в construtor) только для указанных объектов.
Вы также можете написать собственный TypeInformationMapper
например, на основе аннотаций. Если вы аннотируете свой документ с помощью @DocumentType("aliasName")
вы сохраните полиморфизм, сохранив псевдоним класса.
Я кратко объяснил это в своем блоге, но вот небольшой фрагмент кода: https://gist.github.com/athlan/6497c74cc515131e1336
Ответ 5
В то время как ответ Mkyong все еще работает, я хотел бы добавить мою версию решения, поскольку несколько бит устарели и могут оказаться на грани очистки.
Например: MappingMongoConverter(mongoDbFactory(), new MongoMappingContext())
устарел в пользу new MappingMongoConverter(dbRefResolver, new MongoMappingContext());
и SimpleMongoDbFactory(new Mongo(), "databasename");
в пользу new SimpleMongoDbFactory(new MongoClient(), database);
.
Итак, мой окончательный рабочий ответ без предупреждений об отказе:
@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);
}
}
Надеемся, что это поможет людям, которые хотели бы иметь чистый класс без предупреждений об отказе.
Ответ 6
Это однолинейное решение:
@Bean
public MongoTemplate mongoTemplateFraud() throws UnknownHostException {
MongoTemplate mongoTemplate = new MongoTemplate(getMongoClient(), dbName);
((MappingMongoConverter)mongoTemplate.getConverter()).setTypeMapper(new DefaultMongoTypeMapper(null));//removes _class
return mongoTemplate;
}
Ответ 7
Я долго боролся с этой проблемой. Я выполнил подход mkyong, но когда я ввел атрибут LocalDate
(любой класс JSR310 из Java 8), я получил следующее исключение:
org.springframework.core.convert.ConverterNotFoundException:
No converter found capable of converting from type [java.time.LocalDate] to type [java.util.Date]
Соответствующий преобразователь org.springframework.format.datetime.standard.DateTimeConverters
является частью Spring 4.1 и упоминается в Spring Data MongoDB 1.7. Даже если я использовал более новые версии, конвертер не входил.
Решение состояло в том, чтобы использовать существующий MappingMongoConverter
и предоставлять только новый DefaultMongoTypeMapper
(код из mkyong находится под комментарием):
@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)
}
Подводя итог:
- extend
AbstractMongoConfiguration
- аннотировать с помощью
EnableMongoRepositories
- in
mongoTemplate
получить конвертер из базового класса, это гарантирует, что классы преобразования типов зарегистрированы
Ответ 8
вам просто нужно добавить аннотацию @TypeAlias к определению класса вместо изменения отображения типов
Ответ 9
@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;
}
}