Spring Data Reactive Mongo: преобразователь типов классов - PullRequest
0 голосов
/ 23 мая 2018

Я использую Spring boot 2 webflux с реактивным хранилищем Монго.Я пытаюсь сохранить и запросить сущности с типом поля Class<>.Но я не могу понять, как настроить приложение для работы с этим типом.Вот моя сущность, которую я хотел бы сохранить в монго:

import lombok.Data;
import org.springframework.data.annotation.Id;

@Data
public class ServiceEntity {

  @Id
  private String id;
  private String name;
  private Class inputClass;
}

А вот моя конфигурация монго:

import static java.util.Arrays.asList;

import com.mongodb.MongoClient;
import com.mongodb.MongoException;
import com.mongodb.async.client.MongoClientSettings;
import lombok.extern.slf4j.Slf4j;
import org.bson.BsonReader;
import org.bson.BsonWriter;
import org.bson.codecs.Codec;
import org.bson.codecs.DecoderContext;
import org.bson.codecs.EncoderContext;
import org.bson.codecs.configuration.CodecProvider;
import org.bson.codecs.configuration.CodecRegistries;
import org.bson.codecs.configuration.CodecRegistry;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.mongodb.core.convert.MongoCustomConversions;

@Configuration
@Slf4j
public class MongoConfiguration {

  @Bean
  public MongoClientSettings settings() {
    log.debug("Configure mongo settings");
    return MongoClientSettings.builder()
        .codecRegistry(CodecRegistries.fromRegistries(
            CodecRegistries.fromProviders(new ClassCodecProvider()),
            MongoClient.getDefaultCodecRegistry()))
        .build();
  }

  //  @Bean
  public MongoCustomConversions customConversions() {
    log.debug("Configure mongo custom conversions");
    return new MongoCustomConversions(asList(new StringToClassConverter()));
  }

  private static class ClassCodec implements Codec<Class> {

    @Override
    public Class decode(BsonReader reader, DecoderContext decoderContext) {
      try {
        return Class.forName(reader.readString());
      } catch (ClassNotFoundException e) {
        throw new MongoException("Couldn't read value as class type", e);
      }
    }

    @Override
    public void encode(BsonWriter writer, Class value, EncoderContext encoderContext) {
      writer.writeString(value.getName());
    }

    @Override
    public Class<Class> getEncoderClass() {
      return Class.class;
    }
  }

  private static class ClassCodecProvider implements CodecProvider {

    @Override
    public <T> Codec<T> get(Class<T> clazz, CodecRegistry registry) {
      if (clazz == Class.class) {
        return (Codec<T>) new ClassCodec();
      }
      return null;
    }
  }

  private static class StringToClassConverter implements Converter<String, Class> {

    @Override
    public Class convert(String source) {
      try {
        return Class.forName(source);
      } catch (ClassNotFoundException e) {
        throw new MongoException("Couldn't read string as class type", e);
      }

    }
  }

}

Проблема в том, что я пытаюсь прочитать сущности из монго.получить ошибку:

org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.String] to type [java.lang.Class<?>]
    at org.springframework.core.convert.support.GenericConversionService.handleConverterNotFound(GenericConversionService.java:321)
    at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:194)
    at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:174)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.getPotentiallyConvertedSimpleRead(MappingMongoConverter.java:887)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.readValue(MappingMongoConverter.java:1392)

Также я создал конвертер String в класс (см. StringToClassConverter в конфигурации) и попытался использовать его в качестве пользовательского конвертера.Но когда я помещал такую ​​конфигурацию в контекст (чтобы сделать аннотацию @Bean для комментария к методу customConversions()), она использовалась для любого преобразования строки.В результате при сохранении сущностей:

org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.lang.Class<?>] for value 'service1'; nested exception is com.mongodb.MongoException: Couldn't read string as class type
    at org.springframework.core.convert.support.ConversionUtils.invokeConverter(ConversionUtils.java:46)
    at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:191)
    at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:174)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.getPotentiallyConvertedSimpleWrite(MappingMongoConverter.java:849)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writeSimpleInternal(MappingMongoConverter.java:829)
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.writeProperties(MappingMongoConverter.java:488)

Полный код доступен на github .Пожалуйста, см. com.example.mongoclasspertist.ServiceRepositoryTest#test для воспроизведения.

Я вижу следующие способы решения этой проблемы (но они могут привести к другим проблемам, вероятно) путем отправки PR в репозиторий Spring: * To Create ClassToStringConverter и StringToClassConverterв org.springframework.data.mongodb.core.convert.MongoConverters * Для изменения логики метода org.springframework.data.mongodb.core.convert.MappingMongoConverter#getPotentiallyConvertedSimpleWrite для преобразования, только если исходный и целевой типы применимы к пользовательскому преобразователю.

Пожалуйста, помогите мне решить эту проблему.

1 Ответ

0 голосов
/ 23 мая 2018

Использование аннотации @ReadingConverter на преобразователе, кажется, делает эту работу.Пожалуйста, попробуйте.

@ReadingConverter
private static class StringToClassConverter implements Converter<String, Class> 
{
...