使用Firebase实时数据库来快速启动项目并使其启动并运行非常棒。
但是当项目运行时,有时以后,您需要更新自定义对象模型的数据类型,事情就变得不那么令人敬畏了。
比方说,我使用以下模型(kotlin示例)启动了该项目:
@Parcelize
data class CustomModel (
var customField: Int = 0,
)
customField表示布尔值0或1,但为数字
几周后,我意识到此字段可能应该是布尔类型的字段(firebase最初支持)
所以我想将以上模型更改为:
@Parcelize
data class CustomModel (
var customField: Boolean = false,
)
问题是,我已经将customField
(整数)的旧格式的一些数据添加到文档存储中
并使用firebase DataSnapshot的方法:
@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转换为Boolean,反之亦然,以便与较旧的客户端向后兼容
答案 0 :(得分:1)
我认为最简单的解决方案是更改属性的数据类型。根据您的逻辑,可以通过查询数据库以获取该特定属性的所有值并将其存储为布尔值来完成此操作。如果值为0
,则存储false
;如果值为1
,则存储true。获得值后,只需删除属性,然后使用正确的(布尔值)数据类型再次对其进行广告。
编辑::如果您的应用发布了,那是对的,您不能一步一步完成。在这种情况下,您仅应在用户打开应用程序时进行更改。怎么了您可以通过以下机制进行更新:
创建新版本的应用,您可以在其中选择转换属性的数据类型。用户读取该孩子后,将其获取为Int
的值,将其存储在布尔变量中,删除该属性,然后再次正确添加。
通过这种方式,使用旧版本应用程序的用户不会受到影响,因为他们使用的是Int
属性,而使用新版本应用程序的用户将正确使用数据
答案 1 :(得分:0)
因此,我终于想出了解决此数据类型迁移问题的解决方案。
我能够利用kotlin property delegate
来更改我的数据类字段的类型。
首先,我必须将属性的类型更改为想要迁移到的数据类型(在我的情况下是从Int到Boolean),并添加一个自定义属性委托:
@Parcelize
data class CustomModel (
private var _customField: Boolean = false,
): Parcelable {
var customField: Any by FirebaseBooleanDelegate(_customField)
}
这里的属性委托的作用是:代理对我们上面更改的私有属性_customField
的读写访问。
我们也可以使用自定义的getter和setter方法。
代码如下:
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
方法现在能够正确处理数据类型迁移而不会崩溃。