Android Room 编译时警告外键中的列不属于索引。这是啥意思?
Posted
技术标签:
【中文标题】Android Room 编译时警告外键中的列不属于索引。这是啥意思?【英文标题】:Android Room compile-time warning about column in foreign key not part of an index. What does it mean?Android Room 编译时警告外键中的列不属于索引。这是什么意思? 【发布时间】:2017-11-12 19:51:29 【问题描述】:我正在使用最近在 Google I/O 上宣布的 android 架构组件中的 Android 房间持久性库。事情似乎正常,但我收到以下错误:
警告:tagId 列引用了一个外键,但它不是 指数。这可能会在父表位于任何时候触发全表扫描 已修改,因此强烈建议您创建一个涵盖此内容的索引 列。
我的数据库有 3 个表:Note
、Tag
和 JoinNotesTags
。 Notes to Tags 是多对多关系,因此使用 JoinNotesTags 表来处理映射。表格很简单:
Note.id
和 Tag.id
都是主键
JoinNotesTags.noteId
引用 Note.id
JoinNotesTags.tagId
引用 Tag.id
外键约束在JoinNotesTags
表上定义。作为参考,这里是JoinNotesTags
表的CREATE TABLE
语句:
"CREATE TABLE IF NOT EXISTS `JoinNotesTags` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`noteId` INTEGER,
`tagId` INTEGER,
FOREIGN KEY(`noteId`) REFERENCES `Note`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE ,
FOREIGN KEY(`tagId`) REFERENCES `Tag`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION
)"
这是该类对应的@Entity
注解:
@Entity(
indices = arrayOf(Index(value = *arrayOf("noteId", "tagId"), unique = true)),
foreignKeys = arrayOf(
ForeignKey(
entity = Note::class,
parentColumns = arrayOf("id"),
childColumns = arrayOf("noteId"),
onDelete = ForeignKey.CASCADE),
ForeignKey(
entity = Tag::class,
parentColumns = arrayOf("id"),
childColumns = arrayOf("tagId"))
)
)
从@Entity
注释中可以看出,tagId
与noteId
一起包含在复合唯一索引中。我已经确认在自动生成的 json 模式文件中也正确定义了这个索引:
"CREATE UNIQUE INDEX `index_JoinNotesTags_noteId_tagId`
ON `JoinNotesTags` (`noteId`, `tagId`)"
所以,我的问题是:这个警告只是(仍然是 alpha-release)房间库中的一个错误——即编译时分析缺少 tagId
是这个复合索引的一部分这一事实吗?还是我真的有需要解决的索引问题以避免全表扫描?
【问题讨论】:
我们需要查看您的 table 定义,而不是您的 Java 代码。具体来说,我认为错误不是tagId
列没有索引,而是它引用了一个没有索引的外键。
@TimBiegeleisen,已更新问题以包含 JoinNotesTags
的表定义。但为了记录,Tag.id
是一个主键。此外,警告说问题出在tagId
列上——而不是它引用的父表中的列 (Tag.id
)。
【参考方案1】:
在 kotlin 代码中:
之前
@ColumnInfo(name = "question_id")
var questionId: Long
之后
@ColumnInfo(name = "question_id", index = true) //just add index = true
var questionId: Long
【讨论】:
这对我有用。答案可以通过@ColumnInfo
行的前后进行简化以提高可读性。其余的,尤其是 Retrofit 的 @Expose
和 @SerializedName
,是无关紧要的,在大多数情况下不会在数据库实体中使用。【参考方案2】:
如果即使正确定义了索引也会出现此错误,那么除了此之外还有其他一些编译错误。要修复它,只需注释定义关系表中的外键的代码,并制作项目,并修复其他编译错误并取消注释给定的代码。
【讨论】:
【参考方案3】:您需要为列添加索引以获得更快的查询 这是一个例子
@Entity(indices = @Index("artist_id"))
public class Artist
@NonNull
@PrimaryKey
@ColumnInfo(name = "artist_id")
public String id;
public String name;
【讨论】:
【参考方案4】:当您修改Tag
表时,数据库可能需要在JoinNotesTags
表中查找相应的行。为了提高效率,这个requires an index 在tagId
列上。
您的复合索引对此没有用;由于way how indexes work,要搜索的列必须是索引中最左边的列。
您应该只在tagId
列上添加索引。
(您可以交换复合索引中列的顺序,但noteId
会出现同样的问题。)
【讨论】:
能不能加个示例图 引用你的话:You should add an index on only the tagId column
。这是否意味着 noteId 和 tagId 应该是单独的索引?
@AshishKrishnan 是的,两者都应该是索引中最左边的列。
明白了!谢谢。以上是关于Android Room 编译时警告外键中的列不属于索引。这是啥意思?的主要内容,如果未能解决你的问题,请参考以下文章
android.database.sqlite.SQLiteException:没有这样的列:标题:,编译时:
HSQLDB - 在外键中引用 INFORMATION_SCHEMA
Room - 模式导出目录未提供给注释处理器,因此我们无法导出模式
Android ROOM编译时提示错误Schema export directory is not provided to the annotation processor so we cannot(