获取主键而不插入空行

get a primary key without inserting empty rows

所以在我的应用程序中,当用户点击添加某物时,我应该创建一个 实体 A 来承载用户提供的值,这个 实体 A 有一个自动增量主键,在构建 实体 A 的过程中还有另一个实体携带 实体 A [= 的键31=] 作为外键以及它们的组合键的一部分。

所以我的问题是 room 阻止我创建其他实体而不提供 Entity A 的键在他们的构造函数中用 @NonNull 注释它作为它的一部分他们的复合键不能为 null。

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

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

  • 每当用户单击添加选项时,我是否应该 push/insert 一个空的 entity/row/tuple 来获取自动生成的密钥,以便我可以在此过程中创建实体?

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

this Entity A have an autoincremented-primary-key

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

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

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

  • 由于不必要的开销 see 我个人从不使用 autoGenerate = true。
  • AUTOINCREMENT 的作用是让系统 table sqlite_sequence 每个 table 有一行具有 AUTOINCREMENT,其中 stores/maintains 为 table。使用 AUTOINCREMENT 然后它使用 sqlite_sequence 值和最高 rowid 值中的较高者,然后加 1(没有它只使用最高 rowid 并加 1)。

was it a mistake from the beginning to work with my entities as custom classes along my application and I should separate entities from custom classes ?

不需要单独的 classes 实体可以作为独立的 class 使用,房间注释被忽略。

whenever the user clicks the add option, should I just push/insert an empty entity/row/tuple to get an autogenerated key so I could create the entities along the way?

很容易获得生成的密钥和@Insert 用于单个插入 returns 密钥 (id) 很长 所以 @Dao @Insert abstract fun(entityA: EntityA): Long (long in Java) returns key 或 -1 如果 insert 没有插入一行。

如果你使用 list/varargs 作为 @Insert 那么它 return 是一个 Longs 数组,每个元素 returning 插入的键(id)或 -1 .

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

@Entity
data class EntityA(
    @PrimaryKey
    var entityAKey: Long? = null,
    var otherAdata: String
)
  • 没有 AUTOINCREMENT 通过 autoGenerate = true。
  • 没有@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
)

添加一些 Dao 的 :-

@Insert
abstract fun insert(entityA: EntityA): Long
@Insert
abstract fun insert(entityB: EntityB): Long
@Insert
abstract fun insert(entityC: EntityC): Long
  • 注意 Long return 值(如果是 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 工作室的 App Inspector(以前称为 Database Inspector)获得的数据库快照。

你也可以 do/use :-

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

当然,无论何时从数据库中提取,对象都会包含键 (id)(除非您有意选择不包含)。