1.9. расширение функционала на лету
Для реализации такой логики, чтобы при несовпадении полей JSON-объекта с таблицей PostgreSQL данные сохранялись в MongoDB, нужно добавить следующие шаги:
- Конфигурация MongoDB: Подключение MongoDB в Spring Boot.
- Обработка несовпадения полей: При получении JSON проверять, соответствуют ли поля структуре таблицы
UserData
в PostgreSQL. - Сохранение в MongoDB: Если поля не соответствуют, сохранять данные в MongoDB.
Шаг 1: Добавление конфигурации для MongoDB
В файле application.properties
нужно добавить параметры для подключения к MongoDB:
# MongoDB configuration
spring.data.mongodb.uri=mongodb://localhost:27017/somedb
spring.data.mongodb.database=somedb
Шаг 2: Настройка MongoDB модели и репозитория
Создадим модель для хранения произвольных JSON-документов в MongoDB и репозиторий для неё.
Модель для MongoDB:
package com.example.websocket8_4.model;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import java.util.Map;
@Document(collection = "jsondata")
public class JsonData {
@Id
private String id;
private Map<String, Object> data;
// Getters and Setters
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Map<String, Object> getData() {
return data;
}
public void setData(Map<String, Object> data) {
this.data = data;
}
}
Репозиторий для MongoDB:
package com.example.websocket8_4.repository;
import com.example.websocket8_4.model.JsonData;
import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface JsonDataRepository extends ReactiveMongoRepository<JsonData, String> {
}
Шаг 3: Обновление хэндлера
Теперь нужно обновить хэндлер, чтобы он проверял поля JSON-объекта. Если они не соответствуют таблице UserData
в PostgreSQL, данные будут сохранены в MongoDB.
Обновим хэндлер:
package com.example.websocket8_4.handler;
import com.example.websocket8_4.model.UserData;
import com.example.websocket8_4.model.JsonData;
import com.example.websocket8_4.repository.UserDataRepository;
import com.example.websocket8_4.repository.JsonDataRepository;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.reactive.socket.WebSocketHandler;
import org.springframework.web.reactive.socket.WebSocketSession;
import reactor.core.publisher.Mono;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
@Controller
public class JsonSaveHandler implements WebSocketHandler {
@Autowired
private UserDataRepository userDataRepository;
@Autowired
private JsonDataRepository jsonDataRepository;
@Override
public Mono<Void> handle(WebSocketSession session) {
return session.receive()
.map(msg -> msg.getPayloadAsText()) // Получаем текст
.flatMap(this::processJson) // Обрабатываем JSON
.flatMap(result -> session.send(Mono.just(session.textMessage(result)))) // Отправляем результат обратно
.onErrorResume(e -> session.send(Mono.just(session.textMessage("Error: " + e.getMessage())))) // Обрабатываем ошибки
.then();
}
// Метод для обработки JSON
private Mono<String> processJson(String jsonString) {
try {
JsonNode jsonNode = new ObjectMapper().readTree(jsonString);
if (isPostgresCompatible(jsonNode)) {
// Сохраняем в PostgreSQL
String name = jsonNode.get("name").asText();
String message = jsonNode.get("message").asText();
UserData userData = new UserData();
userData.setName(name);
userData.setMessage(message);
return userDataRepository.save(userData)
.map(saved -> "Json Save: Success, ID: " + saved.getId())
.onErrorResume(e -> Mono.just("Json Save: Error: " + e.getMessage()));
} else {
// Сохраняем в MongoDB
Map<String, Object> jsonMap = convertJsonNodeToMap(jsonNode);
JsonData jsonData = new JsonData();
jsonData.setData(jsonMap);
return jsonDataRepository.save(jsonData)
.map(saved -> "Json Save to MongoDB: Success, ID: " + saved.getId())
.onErrorResume(e -> Mono.just("Json Save: Error: " + e.getMessage()));
}
} catch (Exception e) {
return Mono.just("Json Save: Invalid JSON format: " + e.getMessage());
}
}
// Проверка, соответствует ли JSON полям PostgreSQL
private boolean isPostgresCompatible(JsonNode jsonNode) {
return jsonNode.has("name") && jsonNode.has("message");
}
// Метод для конвертации JsonNode в Map
private Map<String, Object> convertJsonNodeToMap(JsonNode jsonNode) {
Map<String, Object> map = new HashMap<>();
Iterator<Map.Entry<String, JsonNode>> fields = jsonNode.fields();
while (fields.hasNext()) {
Map.Entry<String, JsonNode> field = fields.next();
map.put(field.getKey(), field.getValue().asText()); // Конвертируем значения в строку
}
return map;
}
}
Объяснение:
- MongoDB конфигурация: Мы добавили параметры подключения к MongoDB и создали сущность
JsonData
для произвольного хранения JSON-данных в MongoDB. - Логика в хэндлере:
- Проверка JSON: Метод
isPostgresCompatible(JsonNode jsonNode)
проверяет, содержит ли JSON необходимые поля для сохранения в таблице PostgreSQL (name
иmessage
). - Если поля соответствуют, данные сохраняются в PostgreSQL. Если нет — объект сохраняется в MongoDB как документ с произвольными полями.
- Метод
convertJsonNodeToMap(JsonNode jsonNode)
конвертирует произвольный JSON вMap<String, Object>
, чтобы сохранить его в MongoDB.
Шаг 4: Зависимости в pom.xml
Убедитесь, что у вас подключены зависимости для работы с MongoDB и PostgreSQL:
<dependencies>
<!-- Для работы с PostgreSQL -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
<!-- Для работы с MongoDB -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
</dependency>
</dependencies>
Теперь, если в вашем JSON присутствуют поля name
и message
, они будут сохранены в таблице PostgreSQL, в противном случае весь JSON будет сохранён как документ в MongoDB.
application.properties
spring.data.mongodb.host=localhost
spring.data.mongodb.port=27017
spring.data.mongodb.database=somedb
#spring.data.mongodb.username=root
#spring.data.mongodb.password=example
services:
mongo:
image: mongo:latest
restart: always
environment:
# - MONGO_INITDB_ROOT_USERNAME=${MONGO_ROOT_USER}
# - MONGO_INITDB_ROOT_PASSWORD=${MONGO_ROOT_PASSWORD}
- MONGO_INITDB_DATABASE=myDatabase
ports:
- 27017:27017
volumes:
- mongo_data:/data/db
networks:
- mongo-network
mongo-express:
image: mongo-express
environment:
- ME_CONFIG_MONGODB_SERVER=mongo
- ME_CONFIG_MONGODB_PORT=27017
- ME_CONFIG_MONGODB_ENABLE_ADMIN=true
- ME_CONFIG_MONGODB_AUTH_DATABASE=admin
# - ME_CONFIG_MONGODB_AUTH_USERNAME=${MONGO_ROOT_USER}
# - ME_CONFIG_MONGODB_AUTH_PASSWORD=${MONGO_ROOT_PASSWORD}
- ME_CONFIG_BASICAUTH_USERNAME=${MONGOEXPRESS_LOGIN}
- ME_CONFIG_BASICAUTH_PASSWORD=${MONGOEXPRESS_PASSWORD}
depends_on:
- mongo
ports:
- "8081:8081"
networks:
- mongo-network
MONGO_ROOT_USER=root
MONGO_ROOT_PASSWORD=example
MONGOEXPRESS_LOGIN=root
MONGOEXPRESS_PASSWORD=example
но с логинной парой не получилось подключиться к монго поэтому убрал, логин и пароль и всё стало работать.