Исходящий канал MongoDB переопределяет документ в БД

Я использую интеграцию Spring для обновления БД значениями, поступающими в сообщении.

Входящий канал запрашивает базу данных Mongo, набор результатов должен быть обновлен в соответствии со статусом обработки.

Поток работает, но я не получаю ожидаемого поведения.

Я ожидал, что обработчик сообщений, MongoDbStoringMessageHandler, в конечном итоге выполнит обновление, но похоже, что он полностью переопределяет объект в БД с помощью полезной нагрузки сообщения.

В полезной нагрузке есть не все поля, которые присутствуют в БД.

Есть ли какая-то конфигурация, которую мне не хватает? Или это не типичный вариант использования?

Ниже моя конфигурация:

Источник входящего сообщения

@Bean
    public MessageSource mongoMessageSource(MongoDbFactory mongoDbFactory) {
        MongoDbMessageSource mongoDbMessageSource = new MongoDbMessageSource(mongoDbFactory,
                new LiteralExpression(new BasicDBObject("status","test").toString()));
        mongoDbMessageSource.
                setCollectionNameExpression(new LiteralExpression("BankAccountDetail"));
        mongoDbMessageSource.setEntityClass(AccountBalanceDetails.class);
        return mongoDbMessageSource;
    }

Выходной канал для указанного выше источника

@Bean
public IntegrationFlow mongoInboundChannel(MessageSource messageSource) {
    return IntegrationFlows.from(messageSource).
            channel("messageChannel")
            .get();
}

Выходной канал:

@Bean
public MessageChannel messageChannel() {
    return new DirectChannel();
}

Основной поток: -

@Bean
public IntegrationFlow fromDirect(@Qualifier("messageChannel") MessageChannel messageChannel,
                                  AccountBalanceTransformer accountBalanceTransformer,
                                  MongoDbFactory mongoDbFactory, AccountBalanceUpdater accountBalanceUpdater) {
    return IntegrationFlows.from(messageChannel).split().
            handle(accountBalanceUpdater, "update").
            //handle(System.out::println).
            handle(mongoOutput(mongoDbFactory)).
            get();
}

Обработчик исходящего сообщения

@Bean
public MessageHandler mongoOutput(MongoDbFactory mongoDbFactory) {
    MongoDbStoringMessageHandler mongoDbStoringMessageHandler = new MongoDbStoringMessageHandler(mongoDbFactory);
    mongoDbStoringMessageHandler.setCollectionNameExpression(new LiteralExpression("BankAccountDetail"));
    return mongoDbStoringMessageHandler;
}

Вот мой модельный класс: -

@Document(collection = "BankAccountDetail")

открытый класс AccountBalanceDetails {

private String id;
private String status;
private String message;

public String getId() {
    return id;
}

public void setId(String id) {
    this.id = id;
}

public String getStatus() {
    return status;
}

public void setStatus(String status) {
    this.status = status;
}

public String getMessage() {
    return message;
}

public void setMessage(String message) {
    this.message = message;
}

@Override
public String toString() {
    return "AccountBalanceDetails{" +
            "id='" + id + '\'' +
            ", status='" + status + '\'' +
            ", message='" + message + '\'' +
            '}';
}

}

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
0
126
2

Ответы 2

Я смог добиться этого, написав собственный обработчик сообщений.

    public class CustomMongoMessageHandler extends AbstractMessageHandler {

            @Autowired
            private MongoTemplate mongoTemplate;

            @Override
            protected void handleMessageInternal(Message<?> message) throws Exception {
                AccountBalanceDetails accountBalanceDetails = (AccountBalanceDetails) message.getPayload();
                Query basicQuery = new Query();
                mongoTemplate.updateFirst(basicQuery.addCriteria(Criteria.
                   where("id").is(accountBalanceDetails.getId())),
                        Update.update("status", accountBalanceDetails.getStatus()),
                        AccountBalanceDetails.class);
            }
        }

Вместо этого:

@Bean
public IntegrationFlow fromDirect(@Qualifier("messageChannel") MessageChannel messageChannel,
                                  AccountBalanceTransformer accountBalanceTransformer,
                                  MongoDbFactory mongoDbFactory, AccountBalanceUpdater accountBalanceUpdater) {
    return IntegrationFlows.from(messageChannel).split().
            handle(accountBalanceUpdater, "update").
            handle(mongoOutput(mongoDbFactory)).
            get();
}

Сейчас использую:

@Bean
    public IntegrationFlow fromDirect(@Qualifier("messageChannel") MessageChannel messageChannel,
                                      AccountBalanceTransformer accountBalanceTransformer,
                                      MongoDbFactory mongoDbFactory, AccountBalanceUpdater accountBalanceUpdater,
CustomMongoMessageHandler customMongoMessageHandler) {
        return IntegrationFlows.from(messageChannel).split().
                handle(accountBalanceUpdater, "update").
                handle(customMongoMessageHandler()).
                get();
    }

Это действительно upsert для существующего документа, но поскольку вы предоставляете весь документ, вы в конечном итоге переопределяете все его свойства в соответствии с предоставленными полями.

Вы можете рассмотреть возможность использования MongoDbOutboundGateway (MongoDb.outboundGateway() для Java DSL) с соответствующим CollectionCallback:

/**
 * Reference to an instance of {@link CollectionCallback} which specifies the database operation to execute.
 * This property is mutually exclusive with {@link #query} and {@link #queryExpression} properties.
 * @param collectionCallback the {@link CollectionCallback} instance
 * @param <P> the type of the message payload.
 * @return the spec
 */
public <P> MongoDbOutboundGatewaySpec collectionCallback(CollectionCallback<P> collectionCallback) {

https://docs.spring.io/spring-integration/docs/5.0.4.RELEASE/reference/html/mongodb.html#mongodb-outbound-gateway

Мой фактический вариант использования - получить документ от Mongo и передать его через несколько преобразователей, только первый преобразователь получает исходный объект DB, остальные получают другой объект, который имеет идентификатор объекта DB и другие поля. Когда возникает ошибка, я создаю объект POJO коллекции БД и устанавливаю идентификатор, полученный из сообщения, и статус как ошибку. Когда я отправляю этот частично созданный объект в исходящий канал mongo, я получаю поведение, определенное в вопросе.

Amar Dev 05.04.2018 19:02

Я пытаюсь подчеркнуть, что я не предоставляю никаких других свойств, кроме статуса и идентификатора. У меня нет остальных полей, когда может прийти исключение

Amar Dev 05.04.2018 19:04

Правильно, поэтому я предлагаю выполнить тот же partial update, который вы делаете в своем пользовательском коде через упомянутый CollectionCallback в MongoDbOutboundGateway.

Artem Bilan 05.04.2018 19:05

Другие вопросы по теме