Room Database TypeConverter не используется в некоторых коллекциях, например. Набор строк

Итак, у меня есть этот объект данных с набором строк (представляющих неиндексируемые ключевые слова, но здесь это не имеет значения) в качестве члена, и 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 как об ошибке, но пока не получил ответа.

Возможно связанные вопросы здесь и здесь и здесь


person ThePMO    schedule 17.02.2021    source источник
comment
Неважно, во что я его конвертирую (пробовал и то, и другое). Фактически, использование конвертера JSON по-прежнему сохраняется как строка JSON в БД. Дело в том, что Room игнорирует TypeConverter, что бы там ни было.   -  person ThePMO    schedule 04.03.2021


Ответы (1)


Просто используйте String:

@Query("UPDATE $TABLE_NAME SET $COLUMN_STRING_SET= :string WHERE $COLUMN_ID= :id")
fun updateStringSet(id: Long, string: String)
person Martin Zeitler    schedule 09.03.2021
comment
Конечно, в этом случае это может сработать, но это всего лишь обходной путь. Немного обидно делать что-то подобное, я действительно думаю, что это должно работать из коробки. - person ThePMO; 10.03.2021