Сопоставление типов — Spring Data MongoDB

У нас есть вариант использования: мы храним глубоко вложенные объекты в MongoDB. Определенные поля в этом объекте могут иметь разные типы. Например, предположим, что объект может иметь поле, представляющее собой набор InterfaceType, и существуют разные реализации этого, скажем: ConcreteTypeA, ConcreteTypeB с разными полями.

package foo.bar

sealed interface InterfaceType
data class ConcreteTypeA(val aField1: String, val aField2: Int) : InterfaceType
data class ConcreteTypeB(val bField: List<String>) : InterfaceType

При использовании Spring Data Mongo DB для сохранения скажите:

package bar.foo

@Document("myCollection")
data class MyDocument(
    @Id id: String,
    val value: InterfaceType
)

Spring автоматически добавит поля с именем _class, которые помогут создать экземпляр правильного класса при чтении. См. также: Сопоставление типов :: Spring Data MongoDB

{
  "value" : {
    "aField1" : "some value",
    "aField2" : 1234,
    "_class" : "foo.bar.ConcreteTypeA"
  },
  "_class" : "bar.foo.MyDocument"
}

Все это работает нормально, но, конечно, это не очень безопасно для рефакторинга… переименование класса или перемещение его в другой пакет не удастся при чтении документа. Мы могли бы использовать @TypeAlias и зарегистрировать псевдонимы классов в конфигурации, что позволит переименовывать/перемещать класс (при условии, что мы не затрагиваем постоянное значение в аннотации). Или, альтернативно, создайте классы Document с большим количеством полей nullable и обрабатывайте все в репозитории.

Какой рекомендуемый подход к обработке этого типа структуры данных в MongoDB/Spring?

Тот же вопрос существует на Форуме сообщества MongoDB

2
0
77
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Я использую это поведение по умолчанию _class со Spring и Mongo.

По моему опыту, переупаковка/переименование объектов — нечастая проблема. Гораздо более распространенной проблемой является добавление новых полей, не допускающих значение NULL, или переименование/удаление/изменение типа поля.

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

У нас есть сценарии, которые будут обновлять (но не понижать) БД каждый раз, когда мы вносим структурные изменения и быстро просматриваем, и я вижу <3% изменений _class. Эти сценарии написаны вручную; нет ничего более сложного, чем Liquidbase или один из этих менеджеров схемы базы данных.

В случае изменения местоположения класса это так же просто, как:

db.channel.updateMany({"_class" : "old.package.MemberChannel"}, {$set:{"_class" : "new.package.MemberChannel"}})

Большим недостатком этого является то, что перед развертыванием переупакованных/рефакторизованных объектов требуется миграция базы данных, что приводит к простоям. Тогда даже малейшая ошибка при миграции приведет к ошибкам во время выполнения. По моему личному мнению, @TypeAlias будет гораздо более безопасным подходом.

qsi 16.05.2024 10:51
Ответ принят как подходящий

Спасибо AndrewL и qsi за ваш вклад. Мы решили пойти по маршруту @TypeAlias.

Мы также добавили тест с использованием ArchUnit, который гарантирует, что классы, реализующие интерфейс и используемые внутри аннотированного @Document класса, помечены @TypeAlias.

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

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