Рубрики
Без рубрики

Лучший способ хранения значений перечисления в MongoDB

Если вы хотите сохранить значение Java Enum в DB в качестве имени перечисления, то драйвер Mongo поддерживает это. Для экзамена… Помеченный javascript, java, mongodb, enum.

Если вы хотите сохранить значение Java Enum в DB в качестве имени перечисления, то драйвер Mongo поддерживает это. Например, если у вас есть перечисление

public enum ProcessType {
    CONVERT_ONE_TO_ONE,
    CONVERT_ONE_TO_MANY;
}

и он зарегистрирован у поставщика кодеков mongo как

import org.bson.codecs.pojo.ClassModel;
import org.bson.codecs.pojo.PojoCodecProvider;
import org.bson.codecs.pojo.PojoCodecProvider.Builder;
import com.ps2pdf.models.enums.ProcessType; // Local
...
Builder builder = 
ClassModel classModel = ClassModel.builder(ProcessType.class).build();
builder.register(classModel);

затем, всякий раз, когда вы сохраняете экземпляр класса с типом свойства Process Type в DB, результирующий документ Mongo будет иметь строковое значение CONVERT_ONE_TO_ONE или CONVERT_ONE_TO_MANY для этого свойства.

если это все, что вам нужно, то следующее не для вас. В этом случае вы можете следовать Mongo POJO tutorial , чтобы направлять вас.

Ниже приведен способ сохранения значения, связанного с перечислением Java, в MongoDB. Зачем кому-то понадобилось это делать?

  • В Java (также TypeScript) существует соглашение об использовании имен в верхнем регистре в перечислениях, которое, вероятно, унаследовано от использования имен в верхнем регистре для констант.
  • Я предпочитаю присваивать свойствам объекта значения в нижнем регистре (как это делают многие люди)
  • Запретить привязку имени свойства к его значению. Я предпочитаю, чтобы имена переменных были короткими, а присвоенное им значение могло быть любым.

Выше приведено несколько причин сохранения значений перечисления вместо имен в MongoDB.

Еще одним болевым моментом для меня было сравнение декодированных значений Enum во внешнем интерфейсе. Ниже приведено перечисление интерфейса TypeScript для приведенного выше перечисления Java.

export enum WebsocketProcessType {
    CONVERT_ONE_TO_ONE = 'convert-one-to-one',
    CONVERT_ONE_TO_MANY = 'convert-one-to-many',
}

Если бы мы использовали декодер перечислений по умолчанию, предоставляемый драйвером Mongo Java, то наши значения должны совпадать с именами в Java Enum, что слишком сложно и строго для нас, чтобы писать более читаемый код.

Следуя приведенной ниже инструкции и используя Class Transformer для декодирования данных, отправляемых из серверной части, вы сможете легко сопоставлять классы Java с классами TypeScript(js).

Реализация

Шаги:

  1. Создайте и зарегистрируйте поставщика кодеков в реестре Mongocode, который Mongo использует для определения того, какой Enumdecoder использовать значение Java Enum
  2. Создайте и зарегистрируйте декодер перечисления для Типа процесса
  3. Создайте и зарегистрируйте перечисление в базе данных

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

Создайте поставщика кодеков

Я не буду предоставлять импорт, так как у вас должен быть драйвер Mongo Java, а с современными IDE вы можете автоматически импортировать весь импорт.

public class EnumCodecProvider implements CodecProvider {
    @Override
    public  Codec get(Class clazz, CodecRegistry registry) {
        if (clazz == ProcessType.class) {
            return (Codec) new ProcessTypeCodec();
        } 
        return null; // Don't throw here, this tells Mongo this provider doesn't provide a decoder for the requested clazz
    }
}

Это довольно просто. Монго декодер, вызовите get метод поставщика, чтобы получить декодер для класса, который он не знает, как декодировать. Когда он вызывает .... получить(ProcessType.class , МонгоРегисты) |/| мы вернем наш ProcessTypeCodec , который знает, как декодировать ProcessType

CodecRegistry pojoCodecRegistry = 
                fromRegistries(MongoClient.getDefaultCodecRegistry(),
                    CodecRegistries.fromRegistries(
                        CodecRegistries.fromProviders(new EnumCodecProvider())
                    ), 
                );
MongoClientOptions options = MongoClientOptions.builder().codecRegistry(pojoCodecRegistry).build();
// Register above option with the MongoClient

Вышеизложенное регистрирует Поставщик кода перечисления в реестре mongo.

Создайте кодек Enum для кодирования/декодирования нашего Enum

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

abstract class AbstractCodec> implements Codec {
    public AbstractCodec() {
    }

    @Override
    final public void encode(final BsonWriter writer, final T value, final EncoderContext encoderContext) {
        String val = ((Enum) value).toString();
        writer.writeString(val);
    }

    @Override
    final public T decode(final BsonReader reader, final DecoderContext decoderContext) {
        try {
            String value = reader.readString();
            Method method = getEncoderClass().getDeclaredMethod("fromValue", String.class);
            T enumName = (T) method.invoke(null, value);
            return enumName;
        }catch(Exception e) {
            try {
                String value = reader.readString();
                Method method = getEncoderClass().getDeclaredMethod("getDefaultValue");
                T storageType = (T) method.invoke(null, value);
                return storageType;
            } catch (Exception e1) {
                e1.printStackTrace();
            }
            e.printStackTrace();
        }
        return null;
    }

    public abstract Class getEncoderClass();
}

Обратите внимание, что мы вызываем toString в приведенном выше методе encode . Этот метод toString должен быть реализован в классе Process Type Enum, чтобы предоставить значение имени перечисления.

В методе decode мы вызываем fromValue и getDefaultValue | / в нашем ProcessType Enum, чтобы получить имя перечисления, связанное с определенным значением, хранящимся в базе данных. Да, вы должны использовать отражение Java для выполнения метода над объектом класса типа T. Если вам не нравится использовать отражение, вы можете поместить класс decode в

Подводя итог, когда декодер получает запрос со строковым значением, т.е. "convert-one-to-one" , мы получаем имя класса, связанное с этим кодеком, и вызываем статический метод из Value , чтобы получить имя перечисления, соответствующее строковому значению.

Ниже приведен Кодек типа процесса .

public class ProcessTypeCodec extends AbstractCodec {

    public ProcessTypeCodec() {
        super();
    }

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

Это просто позволит Mongo узнать класс, который этот кодек может кодировать/декодировать.

Реализовать и зарегистрировать перечисление типов процессов

public enum ProcessType {
    CONVERT_ONE_TO_ONE("convert-one-to-one"),
    CONVERT_ONE_TO_MANY("convert-one-to-many");

    private String value;
    private static final Map ENUM_MAP;
    static {
        Map map = new HashMap();
        for (ProcessType instance : ProcessType.values()) {
            map.put(instance.value(), instance);
        }
        ENUM_MAP = Collections.unmodifiableMap(map);
    }
    ProcessType(String type) {
        this.value = type;
    }

    public String value() {
        return this.value;
    }

    public static ProcessType fromValue(String value) {
        return ENUM_MAP.get(value);
    }

    /**
     * Used by the Mongo codec
     * 
     * @return
     */
    public static ProcessType getDefaultValue() {
        return CONVERT_ONE_TO_ONE;
    }

    /**
     * Required to properly convert Java Enum name to value.
     * Value is used by front-end and usually uses 
* 1. lowercase
* 2. dashes instead of underscores

*/ @Override public String toString() { return this.value; } }

ENUM_MAP предназначен только для ускорения процесса. Это позволяет нам, декодеру, преобразовать строку в имя перечисления за O(1) временную сложность. По умолчанию вы предпочитаете, я использовал здесь имя перечисления, но обычно это null .

Смотрите выше для регистрации классов в реестре классов Mongo.

Наш PS2PDF Video Compressor принимает некоторые дополнения в качестве Enum для заполнения аргумента команды, необходимого для FFMPEG для сжатия или преобразования видеофайлов. Например, у нас есть перечисление выходного расширения на интерфейсе

export enum OutputExtension {
    MP4 = '.mp4',
    WEBM = '.webm'
}

и на серверной части

public enum OutputExtension {
    MP4(".mp4"),
    WEBM(".webm")
    // ... rest of the code similar to above ProcessType Enum
}

когда мы сохраняем аргумент командной строки, сгенерированный из TypeScript, в DB в документе, он сохраняет фактическое значение, которое мы хотим, т.е. .mp4 расширение в DB. На серверной части наш декодер правильно сопоставляет это значение с соответствующим Java Enum. Когда мы хотим использовать это для создания команды FFMPEG, мы можем фактически использовать значение Enum напрямую. т.е.

class Request { // Sample class that deals with request document stored in DB
    OutputExtension outoutExtenstion;
}

List cmd = List.of("ffmpeg", ..., "-o", Request.outoutExtenstion);
// This generates % ffmpeg ... -o .mp4

Надеюсь, это поможет вам написать более читаемый код. Если вы обнаружите какие-либо ошибки в этом документе, пожалуйста, дайте мне знать, чтобы исправить их.

Оригинал: “https://dev.to/harithay/better-way-to-store-enum-values-in-mongodb-49de”