0

我可以控制由 Android Room 自动创建的“插入/更新/删除”触发器,以在内容和虚拟表之间同步数据。我可以删除不需要的更新触发器吗?

我使用 Android Room 创建了带有外部内容的虚拟表。

内容(正常)表Person

@Entity(tableName = "person")
data class Person(
    @ColumnInfo(name = "name")
    @PrimaryKey
    val name: String,
    @ColumnInfo(name = "phone")
    val phone: String
)

PersonFTS带有内容表的 FTS虚拟表Person

@Entity(tableName = "person_fts")
@Fts4(contentEntity = Person::class)
data class PersonFTS(
    @ColumnInfo(name = "name")
    val name: String,
    @ColumnInfo(name = "phone")
    val phone: String
)

现在,Android Room 已经创建了 4 个触发器来保持数据之间PersonPersonFTS自动同步。这是PersonDatabase_Impl由Android生成的:

public final class PersonDatabase_Impl extends PersonDatabase {
  private volatile PersonDao _personDao;

  @Override
  protected SupportSQLiteOpenHelper createOpenHelper(DatabaseConfiguration configuration) {
    final SupportSQLiteOpenHelper.Callback _openCallback = new RoomOpenHelper(configuration, new RoomOpenHelper.Delegate(3) {
      @Override
      public void createAllTables(SupportSQLiteDatabase _db) {
        _db.execSQL("CREATE TABLE IF NOT EXISTS `person` (`name` TEXT NOT NULL, `phone` TEXT NOT NULL, PRIMARY KEY(`name`))");
        _db.execSQL("CREATE VIRTUAL TABLE IF NOT EXISTS `person_fts` USING FTS4(`name` TEXT NOT NULL, `phone` TEXT NOT NULL, content=`person`)");
        _db.execSQL("CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_person_fts_BEFORE_UPDATE BEFORE UPDATE ON `person` BEGIN DELETE FROM `person_fts` WHERE `docid`=OLD.`rowid`; END");
        _db.execSQL("CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_person_fts_BEFORE_DELETE BEFORE DELETE ON `person` BEGIN DELETE FROM `person_fts` WHERE `docid`=OLD.`rowid`; END");
        _db.execSQL("CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_person_fts_AFTER_UPDATE AFTER UPDATE ON `person` BEGIN INSERT INTO `person_fts`(`docid`, `name`, `phone`) VALUES (NEW.`rowid`, NEW.`name`, NEW.`phone`); END");
        _db.execSQL("CREATE TRIGGER IF NOT EXISTS room_fts_content_sync_person_fts_AFTER_INSERT AFTER INSERT ON `person` BEGIN INSERT INTO `person_fts`(`docid`, `name`, `phone`) VALUES (NEW.`rowid`, NEW.`name`, NEW.`phone`); END");
        _db.execSQL("CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)");
        _db.execSQL("INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'd18709b6afedf9b9ff14af93e6c8157e')");
      } 
....
}

virtual table如果有任何更新,我想避免更新数据content table,所以我需要删除带有名称room_fts_content_sync_person_fts_BEFORE_UPDATEroom_fts_content_sync_person_fts_AFTER_UPDATE. 仅需要 INSERT 和 DELETE 操作的触发器。

Android Room 是否提供这种可能性?

4

1 回答 1

0

我建议在and方法中添加 CallBack 和DROP触发器,以确保您使用(因此不存在的触发器不会导致失败),例如onCreateonOpenDROP TRIGGER IF EXISTS

public static Callback cb = new Callback() {
    @Override
    public void onCreate(@NonNull SupportSQLiteDatabase db) {
        super.onCreate(db);
        db.execSQL("DROP TRIGGER IF EXISTS room_fts_content_sync_person_fts_BEFORE_UPDATE");
        db.execSQL("DROP TRIGGER IF EXISTS room_fts_content_sync_person_fts_AFTER_UPDATE");
    }

    @Override
    public void onOpen(@NonNull SupportSQLiteDatabase db) {
        super.onOpen(db);
        db.execSQL("DROP TRIGGER IF EXISTS room_fts_content_sync_person_fts_BEFORE_UPDATE");
        db.execSQL("DROP TRIGGER IF EXISTS room_fts_content_sync_person_fts_AFTER_UPDATE");
    }

    @Override
    public void onDestructiveMigration(@NonNull SupportSQLiteDatabase db) {
        super.onDestructiveMigration(db);
    }
};

在您的PersonDatabase中,您将包含.addCallback(cb)在 databaseBuilder 中。

您甚至可以使用不需要硬编码的以下内容:-

public static Callback cb = new Callback() {
    @Override
    public void onCreate(@NonNull SupportSQLiteDatabase db) {
        super.onCreate(db);
        getAndDeleteFTSTriggers(db);
    }

    @Override
    public void onOpen(@NonNull SupportSQLiteDatabase db) {
        super.onOpen(db);
        getAndDeleteFTSTriggers(db);
    }

    @Override
    public void onDestructiveMigration(@NonNull SupportSQLiteDatabase db) {
        super.onDestructiveMigration(db);
    }

    public void getAndDeleteFTSTriggers(SupportSQLiteDatabase db) {
        Cursor csr = db.query("SELECT * FROM sqlite_master WHERE type  = 'trigger' AND instr(sql,'room_fts') > 0 AND instr(sql,'_UPDATE') > 0");
        int sqlidx = csr.getColumnIndex("sql");
        while (csr.moveToNext()) {
            db.execSQL(csr.getString(sqlidx).replace("CREATE TRIGGER IF NOT EXISTS","DROP TRIGGER IF EXISTS"));
        }
        csr.close();
    }
};
  • 请注意,上述内容尚未在具有 FTS 的实际数据库上进行测试。但是,它已经在没有 FTS 的数据库上进行了测试(在这种情况下,光标将为空)。因此,您显然应该测试上述内容。
于 2021-11-12T21:23:04.950 回答