PrimaryKey 的autoGenerate 是否完全等同于SQLite 的AUTOINCREMENT?

Is PrimaryKey's autoGenerate exactly equivalent to SQLite's AUTOINCREMENT?

@PrimaryKey(autoGenerate = true) 标记主键是否与在 SQL 语句中使用 PRIMARY KEY AUTOINCREMENT 完全相同?

直觉告诉我是的,但文档似乎表明不是。

房间 javadoc 状态:

Set to true to let SQLite generate the unique id.

好像将其设置为 false 将阻止 SQLite 生成密钥。

但是 SQLite documentation for AUTOINCREMENT 声明 SQLite always 如果在执行 INSERT 时给出 none 则生成当前唯一的键,并且AUTOINCREMENT 只是添加了额外的行为,SQLite 将永远不允许自动生成的键与任何先前删除的行重叠。

SQLite 文档还建议如果不需要(出于性能原因)则不要使用 AUTOINCREMENT,并声明通常不需要。从描述来看,这似乎符合我的情况。如果重新使用之前删除的行 ID,我的 table 就没问题了。

Is marking a primary key with @PrimaryKey(autoGenerate = true) exactly the same as if you had used PRIMARY KEY AUTOINCREMENT in an SQL statement?

,因为使用 autoGenerate=true 添加了 AUTOINCREMENT 关键字。

但是

as if setting it false will prevent SQLite from generating the key.

.

如果 class 是:-

  • 注释为 @Entity
  • column/variable/member 被注释为 @PrimaryKey
  • 如果类型解析为整数类型
    • (byte .... double, primitive or Object (e.g. Double))

然后可以生成值(是INTEGER PRIMARY KEY使该列成为可以生成的特殊列,因为该列是rowid(通常隐藏的列)的别名)。

AUTOINCREMENT 仅适用于 rowid 的别名(即 INTEGER PRIMARY KEY)。它不确定是否可以生成值(在列没有值或值为空时)。

AUTOINCREMENT 的作用是在生成值时添加一条附加规则。该规则是该值必须高于用于该 table.

的任何值

存在细微差别。

没有自动递增

  • 删除具有最高值的行,释放该值以供后续使用(并将用于生成仍然高于当时存在的任何其他值的值),并且
  • 如果达到最高值 (9223372036854775807),SQLite 将尝试找到一个可用的较低值,并且
  • 最后,可以使用负值将值的范围扩大一倍。

使用自动递增

  • 删除具有最高值的行不会释放该值以供后续使用

  • 如果达到最高值 (9223372036854775807),则后续使用生成的值插入的尝试将失败并出现 SQLITE FULL 错误。

    • 如果您插入值为 9223372036854775807 的 1 行,那么这是唯一可以插入的行。
  • 无法生成负值(仍可使用)

  • 需要一个额外的 table (sqlite_sequence),它由 SQLite 自动创建,每个 table 将有一行带有 AUTOINCREMENT。最高使用值存储在行中。因此,每当插入时要生成值时,都需要检索相应的行并获得值,插入后必须更新值。因此,使用 AUTOINCREMENT 会产生开销。

  • 注意 以上是假设绕过 SQLite in-built 处理的方法没有被绕过(例如更新 sqlite_sequence table).

我总是提倡使用(而不是使用autoGenerate=true)例如

@PrimaryKey
Long id_column=null;

@PrimaryKey
var id_column: Long?=null

因此,如果 id_column.

没有给出任何值,将自动生成 @Insert(方便插入)

演示

考虑以下两个带注释的@Entity classes(有和没有autoGenerate=true):-

AutoInc:-

@Entity
data class AutoInc(
    @PrimaryKey(autoGenerate = true)
    val id: Long?=null,
    val other: String
)

NoAutoInc:-

@Entity
data class NoAutoInc(
    @PrimaryKey
    var id: Long?=null,
    var other:String
)

Room(编译后查看class中生成的java与@Database注解class同名)在[=97中有如下内容=]createAllTables method/function:-

    _db.execSQL("CREATE TABLE IF NOT EXISTS `AutoInc` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `other` TEXT NOT NULL)");
    _db.execSQL("CREATE TABLE IF NOT EXISTS `NoAutoInc` (`id` INTEGER, `other` TEXT NOT NULL, PRIMARY KEY(`id`))");

即唯一的区别是 AUTOINCREMENT 关键字。

然后考虑下面的代码:-

    /* Typical  where the id will be generated */
    dao.insert(AutoInc(other = "A"))
    dao.insert(AutoInc(null,other = "B"))
    dao.insert(NoAutoInc(other ="A"))
    dao.insert(NoAutoInc(null, other = "B"))

    /* Beware */
    /* Room interprets types different ways
        here 0 is taken to be 0 as id is an Object
        if long (Java) then 0 will be generated id
        getters/setters are taken in to consideration when determining type
    * */
    dao.insert(AutoInc(0,other = "W"))
    dao.insert(NoAutoInc(0,other ="W"))

    /* Unusual */
    dao.insert(AutoInc(-100,"X"))
    dao.insert(NoAutoInc(-100,other ="X"))
    dao.insert(AutoInc(9223372036854775807,"Y")) /* The maximum value for an id */
    dao.insert(NoAutoInc(9223372036854775807,"Y")) /* The maximum value for an id */

当 运行 时,tables(通过 Android Studio 的 App Inspection)是:-

AutInc:-

注意 Z 行未添加,原因是:-

E/SQLiteLog: (13) statement aborts at 4: [INSERT OR ABORT INTO `AutoInc` (`id`,`other`) VALUES (?,?)] database or disk is full

但是,磁盘未满,如磁盘资源管理器所示:-

它绝不像磁盘资源管理器显示的那样已满(当然,后续步骤会在数据库中插入一行):-

NoAutInc

此处添加了 Z 行,其中生成的 ID 基于 SQLite 发现未使用的值,因为已达到 id 的最高允许值而不是失败由于 disk/table 已满。