Как использовать данные весны с couchbase без атрибута _class

Есть ли простой способ использовать весеннюю базу данных с документами, которые не имеют _class атрибут? В диване у меня что-то подобное в моем sampledata ковш:

{
  "username" : "alice", 
  "created" : 1473292800000,
  "data" : { "a": 1, "b" : "2"},
  "type" : "mydata"
}

Теперь, есть ли способ определить отображение из этой структуры документа в объект Java (обратите внимание, что _class атрибут отсутствует и не может быть добавлен) и наоборот, чтобы я мог получить все (или большинство) автоматических функций из данных рессорной базы?

Что-то вроде: если type Поле имеет значение "mydata", используйте класс MyData.java. Поэтому, когда поиск выполняется вместо автоматического добавления AND _class = "mydata" к сгенерированному запросу добавить AND type = "mydata",

2 ответа

Spring Data в целом нуждается в _class поле, чтобы узнать, что нужно создать обратно при десериализации.

В Spring Data Couchbase довольно легко использовать другое имя поля, чем _classпереопределив typeKey() метод в AbsctractCouchbaseDataConfiguration,

Но он все равно будет ожидать полного имени класса по умолчанию

Чтобы обойти это, потребуется немного больше работы:

  1. Вам нужно будет реализовать свой собственный CouchbaseTypeMapper, следуя модели DefaultCouchbaseTypeMapper, в super(...) конструктор, вам нужно будет предоставить дополнительный аргумент: списокTypeInformationMapper, Реализация по умолчанию явно не предоставляет, поэтому SimpleTypeInformationMapper используется, который является тот, который ставит FQN.
  2. Существует альтернативная реализация, которая настраивается так, что вы можете псевдоним определенных классов с более коротким именем через Map: ConfigurableTypeInformationMapper... 3. Таким образом, поставив ConfigurableTypeInformationMapper с псевдонимом, который вы хотите для определенных классов + SimpleTypeInformationMapper после этого в списке (если вы сериализовали класс, для которого вы не указали псевдоним), вы можете достичь своей цели.
  3. typeMapper используется в MappingCouchbaseConverter, который вы также должны будете расширить, к сожалению (просто для typeMapper вместо по умолчанию.
  4. Как только вы это сделаете, снова переопределите конфигурацию, чтобы вернуть экземпляр вашего пользовательского MappingCouchbaseConverter который использует ваш обычай CouchbaseTypeMapper (mappingCouchbaseConverter() метод).

Вы можете добиться этого, например, создав собственную аннотацию @DocumentType

@DocumentType("billing")
@Document
public class BillingRecordDocument {
    String name;
    // ...
}

Документ будет выглядеть так:

{
    "type" : "billing"
    "name" : "..."
}

Просто создайте следующие классы: Создайте собственный AbstractReactiveCouchbaseConfiguration или AbstractCouchbaseConfiguration (зависит от того, какой вариант вы используете)

@Configuration
@EnableReactiveCouchbaseRepositories
public class CustomReactiveCouchbaseConfiguration extends AbstractReactiveCouchbaseConfiguration {
     // implement abstract methods
     // and configure custom mapping convereter
    @Bean(name = BeanNames.COUCHBASE_MAPPING_CONVERTER)
    public MappingCouchbaseConverter mappingCouchbaseConverter() throws Exception {
        MappingCouchbaseConverter converter = new CustomMappingCouchbaseConverter(couchbaseMappingContext(), typeKey());
        converter.setCustomConversions(customConversions());
        return converter;
    }

    @Override
    public String typeKey() {
        return "type"; // this will owerride '_class'
    }
}

Создать индивидуальный MappingCouchbaseConverter

public class CustomMappingCouchbaseConverter extends MappingCouchbaseConverter {

    public CustomMappingCouchbaseConverter(final MappingContext<? extends CouchbasePersistentEntity<?>,
            CouchbasePersistentProperty> mappingContext, final String typeKey) {
        super(mappingContext, typeKey);
        this.typeMapper = new TypeBasedCouchbaseTypeMapper(typeKey);
    }
}

и пользовательские аннотации @DocumentType

@Persistent
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface DocumentType {

    String value();

}

Затем создайте TypeAwareTypeInformationMapper который просто проверит, аннотирован ли объект @DocumentType если да, используйте значение из этой аннотации, сделайте значение по умолчанию, если нет (полное имя класса)

public class TypeAwareTypeInformationMapper extends SimpleTypeInformationMapper {

    @Override
    public Alias createAliasFor(TypeInformation<?> type) {
        DocumentType[] documentType = type.getType().getAnnotationsByType(DocumentType.class);

        if (documentType.length == 1) {
            return Alias.of(documentType[0].value());
        }

        return super.createAliasFor(type);
    }
}

Затем зарегистрируйте его следующим образом

public class TypeBasedCouchbaseTypeMapper extends DefaultTypeMapper<CouchbaseDocument> implements CouchbaseTypeMapper {

    private final String typeKey;

    public TypeBasedCouchbaseTypeMapper(final String typeKey) {
        super(new DefaultCouchbaseTypeMapper.CouchbaseDocumentTypeAliasAccessor(typeKey),
              Collections.singletonList(new TypeAwareTypeInformationMapper()));
        this.typeKey = typeKey;
    }

    @Override
    public String getTypeKey() {
        return typeKey;
    }
}

В вашем классе конфигурации couchbase вам просто нужно иметь:

@Override
public String typeKey() {
    return "type";
}

К сожалению, для вывода запроса (n1ql) класс или тип по-прежнему используют имя класса. Пробовал пружинный диван 2.2.6, и здесь минус. @ Симон, знаете ли вы, что что-то изменилось, и поддержка, чтобы иметь возможность иметь пользовательское значение _class/type в следующих выпусках?

@SimonBasle Внутри класса N1qlUtils и метода createWhereFilterForEntity у нас есть доступ к CouchbaseConverter. В сети:

String typeValue = entityInformation.getJavaType().getName();

Почему бы не использовать typeMapper из преобразователя для получения имени объекта, если мы не хотим использовать имя класса? В противном случае вам необходимо аннотировать каждый метод в вашем репозитории следующим образом:

@Query("#{#n1ql.selectEntity} WHERE `type`='airport' AND airportname = $1")
List<Airport> findAirportByAirportname(String airportName);

Если createWhereFilterForEntity использовал CouchbaseConverter, мы могли бы избежать аннотирования с помощью @Query.

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