【问题标题】:FMDB: Retrieval of NULL values for value types in SwiftFMDB:在 Swift 中检索值类型的 NULL 值
【发布时间】:2016-01-23 05:40:55
【问题描述】:

使用 FMDB,桥接以在 Swift 中使用,我为这样的 SQLite 列定义检索了长整数值

myColumn   BigInt NULL UNIQUE

为 FMResultSet 提供一行 Swift 代码(基于此处省略的直接选择查询)

let value = resultSet.longForColumnName("myColumn")

这很好用。然而,当我检索并更新涉及该列的多条记录时,我遇到了唯一键索引违规。事实证明,对于 NULL 值,上述 Swift 代码行返回值 0,我看不到正确检测 NULL 值的快速方法。

在寻找处理此问题的正确方法时,我能找到的唯一相关问题是this one concerning empty strings being returned for text columns with Null values。答案不适用于这里。所以,我在这里添加我的研究结果,如果它们对其他人有用。

(事实证明,根本问题并非特定于具有唯一性约束。)

【问题讨论】:

    标签: swift fmdb


    【解决方案1】:

    从 Objective-C 版本桥接的 FMDB API 似乎没有直接的方法来检查 NULL 值(请纠正我,如果我错了,请)。因此,对于定义为的数据库列,例如,

    myColumn   BigInt NULL
    

    NULL 值将在涉及该列的任何 FMResultSet 中显示为值 0,使用 Swift 代码,如问题所示。

    (当碰巧在顶部有一个 UNIQUE 约束时,这将产生特别令人惊讶的结果。NULL 值将从数据库中检索为 0,以便在下一次保存操作时可能会更新,这违反了唯一性涉及多个实体时的约束,如我的情况。但是,基础问题与约束无关。因此,我将重点关注一般的 NULL 值问题。)

    为了避免这个问题,我们必须首先从相应的 FMResultSet 中检索列的值作为对象,如下所示:

    let objectValue = resultSet.objectForColumn("myColumn")
    

    如果objectValue 恰好是 NSNull() 类型/值,那么我们有一个 NULL 值并且可以相应地处理它。否则,我们可以使用普通的longForColumnName 方法。 (然而,对于字符串等对象类型,FMDB 实现自然会返回一个可选项,对于 NULL 的数据库值,该选项将为 nil!)

    为了使这更容易,我使用了 FMResultSet 类的扩展(用于按索引检索,我更喜欢),如下所示:

    extension FMResultSet {
        func isNullForColumnIndex(columnIdx: Int32) -> Bool {
            let value = self.objectForColumnIndex(columnIdx)
            if let nullValue = value as? NSNull {
                return true
            } else {
                return (value == nil)
            }
        }
    }
    

    假设“myColumn”将出现在结果集中的索引 0 处,这会将上面示例中的数字类型的值提取减少为这样的一行:

    let num: Int64? = (result.isNullForColumnIndex(0) ? nil : Int64(result.longForColumnIndex(0)))
    

    当然,我也可以添加一个方法,例如,optionalLongForColumnIndex(columnIndex: Int32) -> Int64?,它将包括 NULL 检查和值检索。这只需要对每种值类型都使用一种这样的方法,到目前为止我一直避免使用这种方法。

    【讨论】:

      【解决方案2】:

      Marco,您自己的答案非常好:它准确地描述了正在发生的事情。然而,在 Swift 中编码时,ccgus/fmdb 有类型更安全的替代方案。你可以试试https://github.com/stephencelis/SQLite.swift(很受欢迎)或https://github.com/groue/GRDB.swift(在精神上更接近fmdb)。

      【讨论】:

      • 感谢您添加这个非常有用的评论。我知道并查看了其中一些,但当时(现在仍然)不完全确定这些包装器在当时(并且仍然)语言的快速变化时有多稳定。处理差异或升级延迟总是很麻烦。此外,在某些情况下,这些框架对 SQLite 的(有些奇怪的)类型处理做出了隐含的假设,特别是在涉及时间戳时。我宁愿确保类型按照我想要的方式映射。请参阅:时间戳映射到字符串和其他类型。
      • 我也非常喜欢这个问题:作为 GRDB 的作者,我现在真的认为我应该停止让 sqlite 在用户写入let i = row.value(named:"foo") as Int 时默默地将 NULL 转换为 0。我的目标是真正防止令人讨厌的意外,这将是一个:-)
      • 您能详细说明您对时间戳的不满吗?同时,我更新了 GRDB.swift 以便 NULL 不能再变成非可选值。我试图尽可能清楚地记录:github.com/groue/GRDB.swift#column-values.
      • 关于那些讨厌的宠物的详细信息,看看这个(我认为不正确的)答案和我的 cmets:stackoverflow.com/questions/7363112/… 我认为 SQLite 的自动类型转换对大多数人来说都是神奇的然而,这并没有意识到实际存储的是什么。就我而言,我想存储大量的时间戳,并且以毫秒为精度。并非所有 SQLite 可以使用的类型都支持毫秒精度,尽管有问题的答案似乎表明它们支持。
      • 我希望 SQLite 的 Swift-y 框架能够做到这一点:以one、定义明确、有效的内部格式以及可预测、一致的结果处理时间戳。文字,对我来说,是不可能的。但是,我记得其他类型不支持毫秒。像 64 位时代这样的东西对我来说没问题,但不支持。总而言之,我确实觉得 SQLite 在这个内部级别滚动的自动类型转换是方便出了问题。比较:Swift 非常严格的类型系统,它甚至不会在兼容的 Integer 类型之间自动转换。是有原因的。
      【解决方案3】:

      我在使用 FMDB 的 Objective-C 中遇到了类似的问题。 使用longForColumnIndex 时也会得到0 而不是nil

      但是使用: NSNumber* object = [resultSet objectForColumnIndex:0]; 按预期工作。

      如果存储了 Value,则提供完全有效的 NSNumber 对象,否则提供 NSNull

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2023-03-16
        • 2021-07-07
        • 2015-08-13
        • 1970-01-01
        • 1970-01-01
        • 2015-01-10
        • 2012-05-23
        相关资源
        最近更新 更多