Итак, у меня есть этот объект данных с набором строк (представляющих неиндексируемые ключевые слова, но здесь это не имеет значения) в качестве члена, и 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"
}
}
Во-первых, я получаю сообщение об ошибке, потому что комната не знает, как обрабатывать эти данные. Честно говоря, давайте реализуем преобразователь типов. Сохранение простоты без преобразования 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>)
}
Однако во время тестирования я обнаружил, что есть проблема под капотом. Сравните две разные реализации преобразования типов один раз в адаптере обновления (который принимает в качестве входных данных полный объект данных, и один раз в пользовательском запросе, ориентированном только на обновление набора строк:
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 недействительна.
Оба вызывают сбои во время выполнения, и их предотвращение должно быть главной задачей!
Возникает вопрос: я что-то делаю не так? Я сообщил об этом в Google как об ошибке, но пока не получил ответа.