web-dev-qa-db-fra.com

Bibliothèque persistante de salle Android: comment insérer une classe comportant un champ d'objet List

Dans Bibliothèque persistante de la pièce Android Comment insérer un objet Model dans un tableau contenant une autre liste.

Laissez-moi vous montrer ce que je veux dire:

@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;
    }
}

mon DAO ressemble à ceci:

@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);
}

Lorsque j'appelle database.CountriesDao().addCountries(countryModel);, j'obtiens l'erreur de compilation de la base de données suivante: Erreur: (58, 31) erreur: Impossible de comprendre comment enregistrer ce champ dans la base de données. Vous pouvez envisager d'ajouter un convertisseur de type pour cela.

devrait-il y avoir une autre table appelée CountryLang? et si oui, comment dire à la pièce de les connecter sur une déclaration insert?

L'objet CountryLang lui-même ressemble à ceci:

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;
    }

}

la réponse ressemble à ceci:

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

Pour chaque pays, il ne va donc pas y avoir plus d'un élément ici. Je suis à l'aise en le désignant pour un seul élément de la liste country_lang. Donc, je peux juste faire une table pour country_lang et ensuite comment le lier à CountryModel. mais comment ? Puis-je utiliser une clé étrangère? J'espérais ne pas avoir à utiliser un fichier plat. alors tu dis que je dois le stocker en json? Est-il recommandé de ne pas utiliser de salle temporaire? quoi utiliser à la place?

19
j2emanue

Comme Omkar l'a dit, vous ne pouvez pas. Je décris ici pourquoi vous devriez toujours utiliser l'annotation @Ignore conformément à la documentation: https://developer.Android.com/training/data-storage/room/referencing-data.html#understand-no-object-references

Vous traiterez l'objet Pays dans une table pour récupérer les données de sa compétence uniquement; Les objets Langues iront à une autre table mais vous pouvez garder le même Dao:

  • Les objets Pays et Langues étant indépendants, définissez simplement la primaryKey avec plus de champs dans l'entité Langage (countryId, languageId). Vous pouvez les enregistrer en série dans la classe Repository lorsque le thread actif est le thread Worker: deux demandes d'insertions vers le Dao.
  • Pour charger l'objet Countries, vous avez countryId.
  • Pour charger les objets de langues liés, vous disposez déjà de countryId, mais vous devez attendre que ce pays soit chargé avant de charger les langues, afin de pouvoir les définir dans l'objet parent et ne renvoyer que l'objet parent.
  • Vous pouvez probablement le faire en série dans la classe Repository lorsque vous chargez le pays. Vous chargerez donc de manière synchrone pays, puis langues, comme vous le feriez côté serveur! (sans librairies ORM).
10
Davideas

Vous pouvez facilement insérer le champ classe avec objet liste en utilisant TypeConverter et 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;
    }
 }

Et ajoutez l'annotation @TypeConverters avant votre champ d'objet de liste,

@Entity(tableName = TABLE_NAME)
public class CountryModel {

//....

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

Pour plus d'informations sur TypeConverters in Room, consultez notre blog ici .

38

Vous ne pouvez pas.

Le seul moyen d'y parvenir est d'utiliser @ForeignKey contrainte. Si vous souhaitez conserver la liste des objets dans votre POJO parent, vous devez utiliser @Ignore ou fournir un @TypeConverter

Pour plus d'informations, suivez ce blog: -

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

et exemple de code: -

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

5
Omkar Amberkar

Voici le convertisseur d'Aman Gupta à Kotlin pour les paresseux Googler qui aiment le copier coller

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)
    }
}
3
Daniel Wilson

Ajoutez @Embedded pour le champ d'objet personnalisé (voir par exemple)

//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 {
    ....
}
0
shashank J