问题描述
所以我有这个数据对象,它有一组字符串(代表不可索引的关键字,但在此处无关紧要)作为成员,当该集合为空时,Room 表现不佳:
@Parcelize
@Entity(tableName = TABLE_NAME)
data class DataItem(
@PrimaryKey
@ColumnInfo(name = COLUMN_ID,index = true) val id: Long,@ColumnInfo(name = COLUMN_GROUP,index = true) var group: Int,@ColumnInfo(name = COLUMN_TEXT,index = true) var text: String,@ColumnInfo(name = COLUMN_STRING_SET) var stringSet: Set<String>
): Parcelable {
companion object
{
const val TABLE_NAME = "TestDataItems"
const val COLUMN_ID = "id"
const val COLUMN_GROUP = "groupColumn"
const val COLUMN_TEXT = "text"
const val COLUMN_STRING_SET = "stringSet"
}
}
首先,我收到一个错误,因为 room 不知道如何处理这些数据。很公平,让我们实现一个类型转换器。在没有 JSON 转换的情况下保持简单,因为我知道 \n
将是一个有效的分隔符。
class TestTypeConverter {
@TypeConverter
fun fromStringSet(value: Set<String>): String {
return value.joinToString ( "\n" )
}
@TypeConverter
fun toStringSet(value: String): Set<String> {
return value.split("\n").toSet()
}
}
Room 接受了我对类型转换的谦虚提议,并从我定义的接口编译了一个不错的 dao 实现。
@androidx.room.Dao
@TypeConverters(value = [TestTypeConverter::class])
interface TestDao{
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertItem(item: DataItem)
@Query("SELECT * FROM $TABLE_NAME")
fun getAllItems() : LiveData<List<DataItem?>>
@Update
fun updateItem(item: DataItem)
@Delete
fun deleteItem(item: DataItem)
@Query("UPDATE $TABLE_NAME SET $COLUMN_STRING_SET= :stringSet WHERE $COLUMN_ID= :id")
fun updateStringSet(id: Long,stringSet: Set<String>)
}
在测试期间,我确实发现引擎盖下存在问题。比较一次在更新适配器中进行类型转换的 2 种不同实现(将完整的数据对象作为输入,一次在仅针对更新字符串集的自定义查询中:
this.__updateAdapterOfDataItem = new EntityDeletionOrUpdateAdapter<DataItem>(__db) {
@Override
public String createQuery() {
return "UPDATE OR ABORT `TestDataItems` SET `id` = ?,`groupColumn` = ?,`text` = ?,`stringSet` = ? WHERE `id` = ?";
}
@Override
public void bind(SupportSQLiteStatement stmt,DataItem value) {
stmt.bindLong(1,value.getId());
stmt.bindLong(2,value.getGroup());
if (value.getText() == null) {
stmt.bindNull(3);
} else {
stmt.bindString(3,value.getText());
}
final String _tmp;
_tmp = __testTypeConverter.fromStringSet(value.getStringSet()); // <-- Room uses my type converter
if (_tmp == null) {
stmt.bindNull(4);
} else {
stmt.bindString(4,_tmp);
}
stmt.bindLong(5,value.getId());
}
};
}
@Override
public void updateStringSet(final long id,final Set<String> stringSet) {
__db.assertNotSuspendingTransaction();
StringBuilder _stringBuilder = StringUtil.newStringBuilder();
_stringBuilder.append("UPDATE TestDataItems SET stringSet= ");
final int _inputSize = stringSet.size();
StringUtil.appendPlaceholders(_stringBuilder,_inputSize);
_stringBuilder.append(" WHERE id= ");
_stringBuilder.append("?");
final String _sql = _stringBuilder.toString();
final SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
int _argIndex = 1;
for (String _item : stringSet) { // <-- Room IGNORES the type converter
if (_item == null) {
_stmt.bindNull(_argIndex);
} else {
_stmt.bindString(_argIndex,_item);
}
_argIndex ++;
}
_argIndex = 1 + _inputSize;
_stmt.bindLong(_argIndex,id);
__db.beginTransaction();
try {
_stmt.executeUpdateDelete();
__db.setTransactionSuccessful();
} finally {
__db.endTransaction();
}
}
因此在自定义查询中room 不使用提供的类型转换器,而是将集合视为列表并希望添加以逗号分隔的字符串值。这会导致一些明显的问题:
- 我的类型转换器使用了不同的分隔符,因此查询不兼容!
- 如果集合为空,则 SQL 字符串无效
两者都会导致运行时故障,防止这些应该是整个空间的重点!
所以问题是:我做错了什么吗?我将其作为 Bug 报告给 Google,但尚未收到任何回复。
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)