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 个表:NoteTagJoinNotesTags。 Notes to Tags 是多对多关系,因此使用 JoinNotesTags 表来处理映射。表格很简单:

Note.idTag.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:没有这样的列:标题:,编译时:

Android Room 迁移未正确处理(错误的列顺序)

如何在Laravel中查看外键中的表字段

HSQLDB - 在外键中引用 INFORMATION_SCHEMA

Room - 模式导出目录未提供给注释处理器,因此我们无法导出模式

Android ROOM编译时提示错误Schema export directory is not provided to the annotation processor so we cannot(