Android Room FTS4 如何删除自动创建的触发器
Android Room FTS4 how to drop triggers created automatically
我能否控制 'insert/update/delete' 由 Android Room 自动创建的触发器以在内容和虚拟 table 之间同步数据。我可以删除不需要的更新触发器吗?
我使用 Android Room 创建了带有外部内容的虚拟 table。
内容(正常)tablePerson
@Entity(tableName = "person")
data class Person(
@ColumnInfo(name = "name")
@PrimaryKey
val name: String,
@ColumnInfo(name = "phone")
val phone: String
)
虚拟 table PersonFTS
用于内容为 table Person
的 FTS
@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 个触发器来自动保持 Person
和 PersonFTS
之间的数据同步。这是 Android 生成的 PersonDatabase_Impl
:
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')");
}
....
}
如果 content table
有任何更新,我想避免更新 virtual table
中的数据,因此我需要删除名称为 room_fts_content_sync_person_fts_BEFORE_UPDATE
和 room_fts_content_sync_person_fts_AFTER_UPDATE
的触发器。仅需要用于 INSERT 和 DELETE 操作的触发器。
Android房间是否提供这种可能性?
我建议在 onCreate
和 onOpen
方法中添加 CallBack 和 DROPing 触发器,确保您使用 DROP 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 中,您将在 databaseBuilder 中包含 .addCallback(cb)
。
您甚至可以使用不需要硬编码的以下内容:-
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 的数据库上进行了测试(在这种情况下 Cursor 将为空)。因此,您显然应该测试以上内容。
我能否控制 'insert/update/delete' 由 Android Room 自动创建的触发器以在内容和虚拟 table 之间同步数据。我可以删除不需要的更新触发器吗?
我使用 Android Room 创建了带有外部内容的虚拟 table。
内容(正常)tablePerson
@Entity(tableName = "person")
data class Person(
@ColumnInfo(name = "name")
@PrimaryKey
val name: String,
@ColumnInfo(name = "phone")
val phone: String
)
虚拟 table PersonFTS
用于内容为 table 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 个触发器来自动保持 Person
和 PersonFTS
之间的数据同步。这是 Android 生成的 PersonDatabase_Impl
:
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')");
}
....
}
如果 content table
有任何更新,我想避免更新 virtual table
中的数据,因此我需要删除名称为 room_fts_content_sync_person_fts_BEFORE_UPDATE
和 room_fts_content_sync_person_fts_AFTER_UPDATE
的触发器。仅需要用于 INSERT 和 DELETE 操作的触发器。
Android房间是否提供这种可能性?
我建议在 onCreate
和 onOpen
方法中添加 CallBack 和 DROPing 触发器,确保您使用 DROP 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 中,您将在 databaseBuilder 中包含 .addCallback(cb)
。
您甚至可以使用不需要硬编码的以下内容:-
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 的数据库上进行了测试(在这种情况下 Cursor 将为空)。因此,您显然应该测试以上内容。