【问题标题】:Room gives error: pre-packaged database has an invalid schema for FTS TableRoom 报错:预打包的数据库对 FTS 表的架构无效
【发布时间】:2020-03-05 16:22:54
【问题描述】:

我正在开发一个字典应用程序,它使用 FTS 表提供搜索。 定义来自预打包的数据库,该数据库具有以下架构:(Linux 上 sqlite3 程序的.schema 的输出)

sqlite> .open words.db
sqlite> .schema
CREATE TABLE entries
    (id INTEGER PRIMARY KEY NOT NULL,
    word TEXT NOT NULL COLLATE NOCASE,
    wordtype TEXT NOT NULL COLLATE NOCASE,
    definition TEXT NOT NULL COLLATE NOCASE);
CREATE INDEX words_index ON entries(id, word);
CREATE VIRTUAL TABLE entriesFts USING FTS4(content='entries', word)
/* entriesFts(word) */;
CREATE TABLE IF NOT EXISTS 'entriesFts_segments'(blockid INTEGER PRIMARY KEY, block BLOB);
CREATE TABLE IF NOT EXISTS 'entriesFts_segdir'(level INTEGER,idx INTEGER,start_block INTEGER,leaves_end_block INTEGER,end_block INTEGER,root BLOB,PRIMARY KEY(level, idx));
CREATE TABLE IF NOT EXISTS 'entriesFts_docsize'(docid INTEGER PRIMARY KEY, size BLOB);
CREATE TABLE IF NOT EXISTS 'entriesFts_stat'(id INTEGER PRIMARY KEY, value BLOB);

我的实体定义如下所示:

@Entity(tableName="entries",
    indices = [Index(name = "words_index", value = ["id", "word"])])
data class Word (
    @PrimaryKey val id :Int,
    @ColumnInfo val word :String,
    @ColumnInfo(name = "wordtype") val wordType :String,
    @ColumnInfo val definition :String
)

/* Used when displaying list of words */
@Fts4(contentEntity = Word::class)
@Entity(tableName = "entriesFts")
data class WordMinimal (
    @PrimaryKey @ColumnInfo(name = "rowid") val id :Int,
    @ColumnInfo val word :String
)

在我看来,模式完全匹配,但 Room 有不同的看法:/
它在 logcat 中引发此错误:

java.lang.IllegalStateException: Pre-packaged database has an invalid schema: entriesFts(rawderm.dictionary.en.db.WordMinimal).
     Expected:
    FtsTableInfo{name='entriesFts', columns=[word], options=[content=`entries`]}
     Found:
    FtsTableInfo{name='entriesFts', columns=[word], options=[content='entries']}

【问题讨论】:

  • `entries`'entries' 不同 - 注意不同的引号
  • 是的,我也是这么想的。但找不到它有什么不同。如果您知道如何解决,请给我链接或发布答案吗?
  • 尝试使用CREATE VIRTUAL TABLE entriesFts USING FTS4(content=entries, word),即没有单引号
  • @MikeT 感谢您的快速回复,我尝试了您首先说的方式,但出现了类似的错误。这次在“找到”...content=entries。一个很好的提示,我使用了content=`entires` ,它奏效了。再次感谢。我想知道我用单引号而不是反引号指的是什么?您能否提供一些参考,因为我无法找到与此相关的任何内容。
  • @AbhijeetSoni uhhm 我不知道有参考。但是,我一直在研究/使用 Convereter thinggy,它将现有的 DB 转换为创建实体的 Room(尽管是 java),所以通过实践学到了一些东西。虽然转换器执行 FTS,但我只测试了 FTS3。不适用于 FTS4,尝试过你的,因为它没有检测到要排除的表(创建虚拟表时自动构建的 FTS 表)。所以目前正在调查。简而言之,知识是通过反复试验:)

标签: android sql android-sqlite android-room fts4


【解决方案1】:

Room 对用于封闭名称的字符非常特别,基本上只接受重音 `(ASCII 码 96)来封闭列名。

对列进行编码最安全的方法是使用重音符号,因为 Room 就是这样做的。说获得准确架构的最简单方法是让 Room 代表您生成架构代码。

您可以通过创建实体和数据库类(即扩展房间数据库的类)然后编译(Ctrl + F9)然后在生成的代码中查找作为数据库类的代码来执行此操作名称附加 _Impl。在那个类中会有一个 createAllTables 方法。这包含 Room 预期的表的 SQL,这是根据实体。

因此 Room 可以方便地生成:-

    _db.execSQL("CREATE TABLE IF NOT EXISTS `entries` (`id` INTEGER NOT NULL, `word` TEXT NOT NULL, `wordtype` TEXT NOT NULL, `definition` TEXT NOT NULL, PRIMARY KEY(`id`))");
    _db.execSQL("CREATE INDEX IF NOT EXISTS `words_index` ON `entries` (`id`, `word`)");
    _db.execSQL("CREATE VIRTUAL TABLE IF NOT EXISTS `entriesFts` USING FTS4(`word` TEXT NOT NULL, content=`entries`)");
    _db.execSQL("CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_entriesFts_BEFORE_UPDATE BEFORE UPDATE ON `entries` BEGIN DELETE FROM `entriesFts` WHERE `docid`=OLD.`rowid`; END");
    _db.execSQL("CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_entriesFts_BEFORE_DELETE BEFORE DELETE ON `entries` BEGIN DELETE FROM `entriesFts` WHERE `docid`=OLD.`rowid`; END");
    _db.execSQL("CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_entriesFts_AFTER_UPDATE AFTER UPDATE ON `entries` BEGIN INSERT INTO `entriesFts`(`docid`, `word`) VALUES (NEW.`rowid`, NEW.`word`); END");
    _db.execSQL("CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_entriesFts_AFTER_INSERT AFTER INSERT ON `entries` BEGIN INSERT INTO `entriesFts`(`docid`, `word`) VALUES (NEW.`rowid`, NEW.`word`); END");
  • 注意它如何另外创建触发器来维护 FTS。并且这些不需要指定为实体或在实体中指定(即,您可以拥有不作为实体存在的 SQLite 组件,并且 Room 不会检测到不匹配)。

【讨论】:

  • 谢谢,这确实很有用。现在把房间放在一边,通过在 SQL 中使用重音或单引号(或者可能只是 SQLite?)我应该得到完全相同的结果吧?
  • @AbhijeetSoni 是的,BUT 仅适用于需要标识符的地方。见SQLite Keywords
猜你喜欢
  • 2021-03-02
  • 1970-01-01
  • 1970-01-01
  • 2020-06-03
  • 1970-01-01
  • 1970-01-01
  • 2021-05-10
  • 1970-01-01
  • 2022-01-10
相关资源
最近更新 更多