【问题标题】:How to declare NUMERIC filed in Room如何在 Room 中声明 NUMERIC 字段
【发布时间】:2021-09-23 04:12:19
【问题描述】:

我有实体:

data class DropsDeposit(
    val userId: Int,
    val articleId: Int,
    val quantity: BigDecimal
)

我的问题是 quantity 字段类型。我使用的是双精度类型,但在这种情况下,我在转换为浮点数时遇到了问题。 所以,a 使用了 BigDecimal,我的问题就消失了。

不幸的是,现在是另一个问题。当我尝试获取该值时,我只得到 6 位数字。 我还有用于从 String 到 BigDecimal 的自定义转换的类型转换器

class BigDecimalTypeConverter {
    @TypeConverter
    fun toBigDecimal(number: String): BigDecimal {
        Timber.e("BigDecimalTypeConverter numberStr: $number")
        return BigDecimal(number, MathContext.UNLIMITED)
    }

    @TypeConverter
    fun fromBigDecimal(number: BigDecimal): String {
        return number.toPlainString()
    }
}

例如

当列数有444.1234567值时,对象值对应的值为444.123

当列数量有0.0004567值时,对象值对应的值为0.0004567

Timber 的日志显示从数据库中获取的值是错误的(就像上面的示例一样)

我想将字段的亲和力设置为 NUMERIC @ColumnInfo(typeAffinity = ColumnInfo.NUMERIC) 但此亲和类型不可用。

有什么办法解决吗?

【问题讨论】:

  • 数量的最大尺寸是多少?
  • @Karol 可能是因为您在类型转换器中使用了 toPlainString() 如果您只使用 toString() 怎么样?
  • @ChinthakaDevinda 问题是从数据库读取而不是转换。 number错了
  • @Eishon 它没有定义,但我认为它大约是 10000。但更重要的是保持正确的转换,所以 double/float 不是正确的解决方案
  • 数量描述了医院的药物剂量,而to通常是一个分数

标签: android sqlite android-sqlite android-room


【解决方案1】:

我想将字段关联设置为 NUMERIC @ColumnInfo(typeAffinity = ColumnInfo.NUMERIC) 但此关联类型不可用。

那是因为 Room 不支持 NUMERIC,它无法解决问题。 NUMERIC 是一个捕获所有类型的关联。最适合的亲和力是 REAL ,它会使用 Float 或 Double 。虽然归根结底,亲和力并没有真正发挥作用,因为 SQLite 可以在任何列中存储任何类型,但重要的是插入数据时生成的 SQL,您几乎无法控制。

基于稍微修改的 DropsDeposit 实体考虑以下内容:-

@Entity
data class DropsDeposit(
    @PrimaryKey
    val userId: Int? = null,
    val articleId: Int = 0,
    val quantity: Double,
    val quantity2: Float
)
  • 演示 Float 和 Double(这是您最终将数据存储为的内容)

    • 另一种选择是将值存储为使用非数字字符的字符串,以防止 SQLite 将其存储为数字值,例如#4444.123456789

标准插入和提取 Dao 的存在(并且为了可重复性删除所有 Dao)。

使用以下代码:-

    db = TheDatabase.getInstance(this)
    dao = db.getAllDao()
    dao.deleteAllDropsDeposit()
    val dec = DecimalFormat("#.0000000000")
    var q: Double = 4444.123456789;
    var q2: Float = 4444.123456789F;
    Log.d("DDINFO","q = ${q}  q2 = ${q2} qf = ${dec.format(q)} qf2 = ${dec.format(q2)}")
    dao.insert(DropsDeposit(quantity = q /*DOUBLE*/,quantity2 = q2 /*FLOAT*/))
    for (d: DropsDeposit in dao.getAllDropsDeposit()) {
        Log.d("DDINFO","q = ${d.quantity}  q2 = ${d.quantity2} qf = ${dec.format(d.quantity)} qf2 = ${dec.format(d.quantity2)}")
    }

然后:-

数据库(通过 Android Studio 的 Database Inspector)显示:-

也就是说,数据存储在数据库中的精度比您所经历的要高(NUMERIC 不会改变这一点)。但是,您会注意到 Float 的 (quantity2) 精度仅为 4dp,并且第 4 位已从 4 舍入到 5。我建议不是 SQLite,而是因为 Float。

现在输出到日志:-

第一行是在插入任何数据之前,是要存储的值。在存储数据之前表明您面临的问题存在。

D/DDINFO: q = 4444.123456789  q2 = 4444.1235 qf = 4444.1234567890 qf2 = 4444.1235351562

与数据的存储方式一致,通过提取数据的输出是相同的

D/DDINFO: q = 4444.123456789  q2 = 4444.1235 qf = 4444.1234567890 qf2 = 4444.1235351562
  • q,Double,不带格式打印也可以。
  • q2,浮点,不带格式打印为 4 dp 并四舍五入。
  • qf, Formatted Double, 除了额外的尾随 0 来满足格式的精度之外很好
  • qf2,格式化浮点数,根据数据格式化打印。

如果代码被修改为在循环之前添加以下内容:-

    q = q + 0.000000000001  - q + 0.9
    q2 = q2 +  0.00001f - q2 + 0.9f
    Log.d("DDINFO","q = ${q}  q2 = ${q2} qf = ${dec.format(q)} qf2 = ${dec.format(q2)}")
    dao.insert(DropsDeposit(quantity = q /*DOUBLE*/,quantity2 = q2 /*FLOAT*/))

那么在插入之前输出到日志的是:-

D/DDINFO: q = 0.9000000000009095  q2 = 0.9 qf = .9000000000 qf2 = .8999999762

数据库:-

提取后的输出与之前相同。

因此,您的问题不在于数据在数据库中的存储方式,而在于类型。 IE。 Float 的局限性。

【讨论】:

    【解决方案2】:

    我不知道你的数量有多大,但是如果在以下范围之间,你可以使用ULong(范围:0到2^64 -1)。

    More info 关于基本类型。

    更新

    如果您必须保持小数输入,请使用 Double。它的范围也与 Long 相同。

    【讨论】:

      猜你喜欢
      • 2019-10-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-04-21
      • 2020-12-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多