Постоянная библиотека в комнате Android - как вставить класс, который имеет поле "Список объектов"

В Android комнате постоянная библиотека, как вставить весь объект Model в таблицу, которая имеет в себе другой список.

Позвольте мне показать вам, что я имею в виду:

@Entity(tableName = TABLE_NAME)
public class CountryModel {

    public static final String TABLE_NAME = "Countries";

    @PrimaryKey
    private int idCountry;

    private List<CountryLang> countryLang = null;

    public int getIdCountry() {
        return idCountry;
    }

    public void setIdCountry(int idCountry) {
        this.idCountry = idCountry;
    }

    public String getIsoCode() {
        return isoCode;
    }

    public void setIsoCode(String isoCode) {
        this.isoCode = isoCode;
    }

    /** 
        here i am providing a list of coutry information how to insert 
        this into db along with CountryModel at same time 
    **/
    public List<CountryLang> getCountryLang() {
        return countryLang;
    }

    public void setCountryLang(List<CountryLang> countryLang) {
        this.countryLang = countryLang;
    }
}

мой DAO выглядит так:

@Dao
public interface CountriesDao{

    @Query("SELECT * FROM " + CountryModel.TABLE_NAME +" WHERE isoCode =:iso_code LIMIT 1")
    LiveData<List<CountryModel>> getCountry(String iso_code);

    @Query("SELECT * FROM " + CountryModel.TABLE_NAME )
    LiveData<List<CountryModel>> getAllCountriesInfo();

    @Insert(onConflict = REPLACE)
    Long[] addCountries(List<CountryModel> countryModel);

    @Delete
    void deleteCountry(CountryModel... countryModel);

    @Update(onConflict = REPLACE)
    void updateEvent(CountryModel... countryModel);
}

Когда я вызываю database.CountriesDao().addCountries(countryModel); я получаю следующую ошибку компиляции базы данных: Ошибка: (58, 31) ошибка: Не могу понять, как сохранить это поле в базе данных. Вы можете рассмотреть возможность добавления конвертера типов для него.

должна быть еще одна таблица с именем CountryLang? и если да, то как указать номер, чтобы связать их в инструкции вставки?

Сам объект CountryLang выглядит так:

public class CountryLang {


    private int idCountry;

    private int idLang;

    private String name;

    public int getIdCountry() {
        return idCountry;
    }

    public void setIdCountry(int idCountry) {
        this.idCountry = idCountry;
    }

    public int getIdLang() {
        return idLang;
    }

    public void setIdLang(int idLang) {
        this.idLang = idLang;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

ответ выглядит так:

"country_lang": [
      {
        "id_country": 2,
        "id_lang": 1,
        "name": "Austria"
      }
    ]

Для каждой страны, поэтому здесь не будет больше одного пункта. Мне удобнее называть его только одним элементом в списке country_lang. Так что я могу просто сделать таблицу для country_lang, а затем кое-как связать ее с CountryModel. но как? я могу использовать внешний ключ? Я надеялся, что мне не нужно было использовать плоский файл. так ты говоришь, я должен хранить его как JSON? Рекомендуется не использовать комнату для временного? что использовать вместо этого?

Ответ 1

Как сказал Омкар, ты не можешь. Здесь я опишу, почему вы всегда должны использовать аннотацию @Ignore соответствии с документацией: https://developer.android.com/training/data-storage/room/referencing-data.html#understand-no-object-references

Вы будете обрабатывать объект Country в таблице для получения данных только о его компетенции; Объекты Languages попадут в другую таблицу, но вы можете оставить тот же Дао:

  • Объекты Страны и Языки независимы, просто определите primaryKey с большим количеством полей в объекте Language (countryId, languageId). Вы можете сохранить их последовательно в классе Repository, когда активный поток является рабочим потоком: два запроса вставок в Dao.
  • Для загрузки объекта Страны у вас есть идентификатор страны.
  • Для загрузки связанных объектов Languages у вас уже есть countryId, но вам нужно будет подождать, пока эта страна будет загружена, прежде чем загружать языки, чтобы вы могли установить их в родительском объекте и вернуть только родительский объект.
  • Вероятно, вы можете делать это последовательно в классе Repository при загрузке страны, поэтому вы будете загружать синхронно страну и затем языки, как вы делали бы на стороне сервера! (без библиотек ORM).

Ответ 2

Вы можете легко вставить класс с полем списка объектов, используя TypeConverter и GSON,

public class DataConverter {

    @TypeConverter
    public String fromCountryLangList(List<CountryLang> countryLang) {
        if (countryLang == null) {
            return (null);
        }
        Gson gson = new Gson();
        Type type = new TypeToken<List<CountryLang>>() {}.getType();
        String json = gson.toJson(countryLang, type);
        return json;
    }

    @TypeConverter
    public List<CountryLang> toCountryLangList(String countryLangString) {
        if (countryLangString == null) {
            return (null);
        }
        Gson gson = new Gson();
        Type type = new TypeToken<List<CountryLang>>() {}.getType();
        List<CountryLang> countryLangList = gson.fromJson(countryLangString, type);
        return countryLangList;
    }
 }

И добавьте аннотацию @TypeConverters перед полем списка объектов,

@Entity(tableName = TABLE_NAME)
public class CountryModel {

//....

@TypeConverters(DataConverter.class)
    public List<CountryLang> getCountryLang() {
        return countryLang;
    }
//....
} 

Для получения дополнительной информации о преобразователях типов в комнате посетите наш блог здесь и официальные документы.

Ответ 3

Вот конвертер Aman Gupta в Котлине для ленивых Гуглер, которые любят копирование:

class DataConverter {

    @TypeConverter
    fun fromCountryLangList(value: List<CountryLang>): String {
        val gson = Gson()
        val type = object : TypeToken<List<CountryLang>>() {}.type
        return gson.toJson(value, type)
    }

    @TypeConverter
    fun toCountryLangList(value: String): List<CountryLang> {
        val gson = Gson()
        val type = object : TypeToken<List<CountryLang>>() {}.type
        return gson.fromJson(value, type)
    }
}

Ответ 4

Ты не можешь.

Единственный способ добиться этого - использовать ограничение @ForeignKey. Если вы хотите сохранить список объектов внутри вашего родительского POJO, вы должны использовать @Ignore или предоставить @TypeConverter

Для получения дополнительной информации, следуйте этому сообщению в блоге: -

https://www.bignerdranch.com/blog/room-data-storage-for-everyone/

и пример кода: -

https://github.com/googlesamples/android-architecture-components

Ответ 5

Добавьте @Embedded для поля пользовательского объекта (см., Например, следующий)

//this class refers to pojo which need to be stored
@Entity(tableName = "event_listing")
public class EventListingEntity implements Parcelable {

    @Embedded  // <<<< This is very Important in case of custom obj 
    @TypeConverters(Converters.class)
    @SerializedName("mapped")
    public ArrayList<MappedItem> mapped;

    //provide getter and setters
    //there should not the duplicate field names
}

//add converter so that we can store the custom object in ROOM database
public class Converters {
    //room will automatically convert custom obj into string and store in DB
    @TypeConverter
    public static String 
    convertMapArr(ArrayList<EventListingEntity.MappedItem> list) {
    Gson gson = new Gson();
    String json = gson.toJson(list);
    return json;
    }

  //At the time of fetching records room will automatically convert string to 
  // respective obj
  @TypeConverter
  public static ArrayList<EventsListingResponse.MappedItem> 
  toMappedItem(String value) {
    Type listType = new 
     TypeToken<ArrayList<EventsListingResponse.MappedItem>>() {
    }.getType();
    return new Gson().fromJson(value, listType);
  }

 }

//Final db class
@Database(entities = {EventsListingResponse.class}, version = 2)
@TypeConverters({Converters.class})
public abstract class AppDatabase extends RoomDatabase {
    ....
}