DevUA

R&D engineer at Echovox

Meta


Spring Data MongoDB avoid MappingException

Stepan VertepniyStepan Vertepniy

В нашому проекті ми використовуємо звязку Mongo 3.x і  обгортку Spring Data MongoDB використовуючи як кеш зі стрічковими ключами. Нещодавно в логах продакшина ми знайшли цікаву  помилку, враховуючи, що код для роботи з кешом вже кілька років незмінний, то це було для нас несподіванкою. Отож що означає таке повідомлення та як це виправити

org.springframework.data.mapping.model.MappingException: Map key .......... contains dots but no replacement was configured! Make sure map keys don't contain dots in the first place or configure an appropriate replacement!

Щоб зрозуміти чому виникає подібна помилка вернемось до специфікації. Mongo це документо-орієнтована база даних в якій сутності зберігаються у вигляді BSON документів, що віддаються користувачам у JSON форматі. В самому документі ключем є дані рядкового типу на які накладені певні обмеження

[ecko_quote source=”https://docs.mongodb.org/manual/core/document/”]Field Names

Field names are strings.

Documents have the following restrictions on field names:

Обмеження на використання символу “.” в назві пов’язане з тим, що крапка використовується для доступу до полів вкладених обєктів, які можуть міститись в документі.

https://docs.mongodb.org/manual/reference/glossary/#term-dot-notation

Тому в спрінговому MongoTemplate ввели заміну можливого символу “.” в значеннях ключа.

Реалізує дану заміну метод potentiallyEscapeMapKey(String source) в класі MappingMongoConverter.java.

/**
	 * Potentially replaces dots in the given map key with the configured map key replacement if configured or aborts
	 * conversion if none is configured.
	 * 
	 * @see #setMapKeyDotReplacement(String)
	 * @param source
	 * @return
	 */
	protected String potentiallyEscapeMapKey(String source) {

		if (!source.contains(".")) {
			return source;
		}

		if (mapKeyDotReplacement == null) {
			throw new MappingException(String.format(
					"Map key %s contains dots but no replacement was configured! Make "
							+ "sure map keys don't contain dots in the first place or configure an appropriate replacement!",
					source));
		}

		return source.replaceAll("\\.", mapKeyDotReplacement);
	}

Як ми бачимо йде заміна “.” на mapKeyDotReplacement

 return source.replaceAll("\\.", mapKeyDotReplacement);

Відповідно для коректної роботи необхідно щоб було ініціалізовано поле mapKeyDotReplacement. У противному випадку ми отримаєм помилку.

Отож необхідно правильно сконфігурувати MappingMongoConverter інакше SpringMongoData  візьме значення по замовчування де  mapKeyDotReplacement є порожнім.

Для конфігурації *.xml “форматі”:

	<bean id="mongoMoxydomainConverter" class="org.springframework.data.mongodb.core.convert.MappingMongoConverter">
		<constructor-arg index="0" ref="mongoDbFactory" />
		<constructor-arg index="1">
			<bean class="org.springframework.data.mongodb.core.mapping.MongoMappingContext" />
		</constructor-arg>
		<property name="mapKeyDotReplacement" value="\\+" />
	</bean>

де задаєм необхідне значення поля mapKeyDotReplacement

<property name="mapKeyDotReplacement" value="\\+" />

Якщо використовується java конфігурація то це можна зробити ось так

public @Bean MongoTemplate mongoTemplate() throws Exception {

		MappingMongoConverter converter = new MappingMongoConverter(new DefaultDbRefResolver(mongoDbFactory()),
				new MongoMappingContext());
		converter.setMapKeyDotReplacement("\\+");

		MongoTemplate mongoTemplate = new MongoTemplate(mongoDbFactory(), converter);

		return mongoTemplate;

	}

Загалом доволі очевидна річ з крапкою у ключі може змусити похвилюватись, якщо ви недостатньо уважні і забули правильно ініціалізувати конвертер. На щастя крапка в ключах не є такою частою оказією і в нашому випадку ми прожили три роки до першої помилки в продакшині.

Тож будьте уважні і читайте специфікації  🙂

R&D engineer at Echovox