【问题标题】:get a primary key without inserting empty rows获取主键而不插入空行
【发布时间】:2021-10-31 20:05:01
【问题描述】:

所以在我的应用程序中,当用户点击添加某些东西时,我应该创建一个 Entity A 来携带用户提供的值,这个 Entity A 具有自动递增的-primary-key,同样在构造Entity A的过程中,还有另一个Entities携带Entity A的键作为外键以及它们的组合的一部分键。

所以我的问题是该房间阻止我创建其他实体,而无需在构造函数中提供 Entity A 的键并用 @NonNull 对其进行注释,因为它是它们的复合键的一部分,并且可以t 为空。

现在我不知道如何解决这个问题,

  • 从一开始就将我的实体作为自定义类在我的应用程序中使用是一个错误,我应该将实体与自定义类分开吗? (尽管他们将拥有相同的字段)

  • 每当用户单击添加选项时,我是否应该只推/插入一个空实体/行/元组以获取自动生成的密钥,以便我可以一路创建实体?

请告诉我您对此的看法,因为这是我第一次使用嵌入在应用程序中的数据库,所以我不知道应该如何看待它。

【问题讨论】:

    标签: android sqlite android-room android-architecture-components


    【解决方案1】:

    这个实体 A 有一个自动增量主键

    AUTOINCREMENT,在 Room autoGenerate = true 中作为 @PrimaryKey 注释的一部分,实际上不会导致自动生成。相反,它是一个约束规则,强制下一个自动生成的 rowid 大于任何现有或已经存在的(对于该表)。

    如果列是 INTEGER PRIMARY KEY(或通过诸如 PRIMARY KEY 之类的列的表级定义隐含),则不使用 AUTOINCREMENT,则该列将成为始终存在的 rowid 的别名(除了很少使用 WITHOUT ROWID 表(无法通过实体在 Room 中这样做,这样的表没有注释))

    rowid 始终是唯一的,并且始终自动生成,并且通常会更大(通常大 1)。只有在 AUTOINCREMENT 发挥作用时达到最大值(9223372036854775807th rowid)时(除非故意操纵)。在这种情况下,使用 AUTOINCREMENT 你会得到一个 SQLITE_FULL 异常,没有 SQLITE 会尝试找到一个较低的未使用/空闲 rowid。

    • 由于不必要的开销see 我个人从不使用 autoGenerate = true。
    • AUTOINCREMENT 的作用是有一个系统表 sqlite_sequence,每个表都有一行,它有 AUTOINCREMENT,它存储/维护表的最高分配 rowid。使用 AUTOINCREMENT 然后它使用 sqlite_sequence 值和最高 rowid 值中的较高者,然后加 1(没有它只使用最高 rowid 并加 1)。

    从一开始就将我的实体作为自定义类在我的应用程序中使用是一个错误,我应该将实体与自定义类分开吗?

    不需要有单独的类,实体可以用作独立类,房间注释被忽略。

    每当用户单击添加选项时,我是否应该只推/插入一个空实体/行/元组以获取自动生成的密钥,以便我可以一路创建实体?

    很容易获得生成的键,并且单个插入的@Insert 将键(id)返回为long,因此@Dao @Insert abstract fun(entityA: EntityA): Long (long in Java) 返回键,如果插入没有插入行,则返回-1。

    如果您使用@Insert 的列表/可变参数,那么它会返回一个Long 数组,每个元素返回插入的键(id)或-1。

    因此,考虑到我认为您的问题,请考虑以下 3 个实体(如果 Java 则使用 Long 而不是 long 作为键,因为原语不能为空)。

    @Entity
    data class EntityA(
        @PrimaryKey
        var entityAKey: Long? = null,
        var otherAdata: String
    )
    
    • 没有通过 autoGenerate = true 进行 AUTOINCREMENT。
    • 没有@NOTNULL 注释

    然后:-

    @Entity
    data class EntityB(
        @PrimaryKey
        var entityBKey: Long?= null,
        var otherBdata: String
    )
    

    和:-

    @Entity(
        primaryKeys = ["entityBRef","entityARef","otherPartOfPrimaryKey"]
    )
    data class EntityC(
        var entityBRef: Long,
        var entityARef: Long,
        var otherPartOfPrimaryKey: Long,
        var otherCData: String
    )
    

    添加一些道的:-

    @Insert
    abstract fun insert(entityA: EntityA): Long
    @Insert
    abstract fun insert(entityB: EntityB): Long
    @Insert
    abstract fun insert(entityC: EntityC): Long
    
    • 注意 Long 返回值(如果是 Int,则始终 Long 不会编译)和生成的密钥无论如何都应该很长,因为它们可能超过 Int 可以容纳的值。

    最后考虑:-

        db = TheDatabase.getInstance(this)
        dao = db.getDao()
        
        var myfirstA = EntityA(otherAdata = "First")
        var myfirstB = EntityB(otherBdata = "The first B")
        var a1 = dao.insert(myfirstA)
        var b1 = dao.insert(myfirstB)
        dao.insert(EntityC(b1,a1,100L,"The first C using id's from the first A and the first B"))
    
    • 通过allowMainThreadQueries()在主线程上运行

    还有数据库:-

    你甚至可以这样做:-

        dao.insert(EntityC(
            dao.insert(EntityB(otherBdata = "Second B")), 
            dao.insert(EntityA(otherAdata = "A's second")), 
            200,
            "blah")
        )
    
    • 显然这可能用途有限,因为您需要预先知道这些值。

    结果是:-

    • 通过 Android Studio 的 App Inspector(以前称为 Database Inspector)获取的数据库快照。

    你也可以做/使用:-

        var my3rdA = EntityA(otherAdata = "3rd")
        my3rdA.entityAKey = dao.insert(my3rdA)
    

    当然,当您从数据库中提取时,对象将包含键 (id)(除非您故意选择不这样做)。

    【讨论】:

    • 从没想过我会得到这么长的答案,谢谢伙计,我想将 Long 更改为它的原语会暂时解决我的问题,每当我插入主实体并获得键值时,我都会更改在其他实体中的价值
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-11
    相关资源
    最近更新 更多