Как изящно поддерживать изменение типа данных в базе данных Firebase? - PullRequest
0 голосов
/ 18 февраля 2019

Использование базы данных Firebase в реальном времени для быстрого запуска проекта и его запуска и запуска - это прекрасно.

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

Скажем, я запустил проект со следующей моделью (пример kotlin):

@Parcelize
data class CustomModel (
    var customField: Int = 0,
)

customField, представляющий логическое значение 0 или1, но как число

Через несколько недель я понимаю, что это поле должно быть логического типа (изначально поддерживается firebase)

Поэтому я хочу изменить приведенную выше модель на:

@Parcelize
data class CustomModel (
    var customField: Boolean = false,
)

Проблема в том, что я уже добавил некоторые данные со старым форматом для моего customField (Int) в мое хранилище документов

И с помощью метода DataSnapshot в Firebase:

@PublicApi
public <T> T getValue(@NonNull Class<T> valueType)

приводит к com.google.firebase.database.DatabaseException: Failed to convert value of type java.lang.Int to Boolean

Итак, вопрос прост, как мы можем справиться с миграцией схемы базы данных в Firebase?

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

Ответы [ 2 ]

0 голосов
/ 04 марта 2019

Итак, я наконец-то нашел чистое решение этой проблемы миграции типов данных.Мне удалось использовать kotlin property delegate для изменения типа поля моего класса данных.

Сначала мне пришлось изменить тип моего свойства на тип данных, на который я хотел перейти(в моем случае от Int до Boolean) и добавьте делегат пользовательского свойства:

@Parcelize
data class CustomModel (
    private var _customField: Boolean = false,
): Parcelable {
    var customField: Any by FirebaseBooleanDelegate(_customField)
}

Роль делегата свойства здесь: прокси для доступа на чтение и запись к теперь частному свойству _customFieldмы изменились выше.Мы также могли бы использовать собственные методы получения и установки.

Вот код:

class FirebaseBooleanDelegate(var isOn: Boolean) : ReadWriteProperty<Any?, Any> 
{

    override fun getValue(thisRef: Any?, property: KProperty<*>): Any {
        return isOn
    }

    override fun setValue(thisRef: Any?, property: KProperty<*>, value: Any) {
        Log.d("FirebaseBooleanDelegate", "calling setter of ${property.name} with value $value")
        isOn = helperSet(value)
    }
}

private fun <T> helperSet(t: T) = when (t) {
    is Number -> t.toBoolean()
    is Boolean -> t
    else -> throw IllegalArgumentException()
}

В методе helperSet у меня есть логика, обрабатывающая миграцию типов данных из Number в Boolean.

Наконец, и потому что мой класс данныхсвойство _customField теперь закрытое, мне пришлось написать собственный парсер для моего класса @Parcelized, иначе мое новое приватное поле не будет сериализовано обратно в firebase:

private companion object : Parceler<CustomModel> {
    override fun CustomModel.write(parcel: Parcel, flags: Int) {
        parcel.writeByte((if (customField as Boolean) 1 else 0).toByte())
    }

    override fun create(parcel: Parcel): CustomModel {
        return CustomModel(
                _customField = parcel.readByte() != 0.toByte()
        )
    }
}

При всех этих настройках Datasnapshot's getValueТеперь метод может правильно обрабатывать миграцию типов данных без сбоев.

0 голосов
/ 18 февраля 2019

Самое простое решение, которое я могу придумать, - это изменить тип данных свойства.Это можно сделать, запросив базу данных, чтобы получить все значения этого конкретного свойства и сохранить его в логическое значение, в соответствии с вашей логикой.Если значение равно 0 store false, если значение равно 1 store true.Получив значения, просто удалите свойство и повторите его, указав правильный (логический) тип данных.

Редактировать: Если ваше приложение выпущено, вы правы, вы не можете сделатьэто за один шаг.В этом случае вы должны вносить изменения только тогда, когда пользователь открывает приложение.Какой поток?Обновление выполняется по следующему механизму:

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...