【发布时间】:2019-03-03 20:48:00
【问题描述】:
我的房间数据库有以下实体:
@Entity
public class SmsMessage {
@PrimaryKey
public long id;
public boolean incoming;
public String sender;
public String receiver;
public String body;
public long timestamp;
}
尝试向数据库中插入多个项目时,此操作失败并显示以下 RuntimeException:
SQLiteConstraintException: PRIMARY KEY must be unique (code 19)
生成的 SQL CREATE TABLE 语句如下所示:
CREATE TABLE `SmsMessage` (
`id` INTEGER NOT NULL,
`incoming` INTEGER NOT NULL,
`sender` TEXT,
`receiver` TEXT,
`body` TEXT,
`timestamp` INTEGER NOT NULL,
PRIMARY KEY(`id`)
);
这似乎与 INTEGER NOT NULL PRIMARY KEY 不同,尽管我在 the SQLite documentation 中找不到任何有关此行为的文档。
看来我必须使用 @PrimaryKey (autogenerate=true) 才能让 Room 自动生成主键值。查看使用autogenerate=true时生成的数据库,会生成以下SQL:
CREATE TABLE `SmsMessage` (
`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
`incoming` INTEGER NOT NULL,
`sender` TEXT,
`receiver` TEXT,
`body` TEXT,
`timestamp` INTEGER NOT NULL
);
看来autogenerate=true对应SQLiteAUTOINCREMENT。但是,the SQLite documentation 非常清楚地表明不需要(并且在大多数情况下不推荐)AUTOINCREMENT 来自动生成唯一的主键。 AUTOINCREMENT 的目的基本上是为了防止重复使用已使用但已删除的主键。
AUTOINCREMENT 关键字会带来额外的 CPU、内存、磁盘空间和磁盘 I/O 开销,如果不是严格需要,应该避免。通常不需要。
在 SQLite 中,类型为 INTEGER PRIMARY KEY 的列是 ROWID 的别名(除了 WITHOUT ROWID 表),它始终是 64 位有符号整数。
在 INSERT 中,如果 ROWID 或 INTEGER PRIMARY KEY 列没有显式地给定一个值,那么它将用一个未使用的整数自动填充,通常比当前使用的最大 ROWID 多一个。无论是否使用 AUTOINCREMENT 关键字都是如此。
如果 AUTOINCREMENT 关键字出现在 INTEGER PRIMARY KEY 之后,则会更改自动 ROWID 分配算法,以防止在数据库的生命周期内重复使用 ROWID。换句话说,AUTOINCREMENT 的目的是防止重复使用以前删除的行中的 ROWID。
所以似乎通常不需要也不推荐使用@PrimaryKey(autogenerate=true)。但仅单独使用@PrimaryKey 根本不会自动生成值。
如何告诉 Room 我想要的是 'id' INTEGER NOT NULL PRIMARY KEY?
【问题讨论】:
标签: android sqlite android-sqlite android-room