web-dev-qa-db-de.com

Persistente Android-Raumbibliothek - Einfügen einer Klasse mit einem Listenobjektfeld

In Android-Raum persistente Bibliothek wie man das gesamte Model-Objekt in eine Tabelle einfügt, die eine andere Liste in sich hat.

Lass mich dir zeigen, was ich meine:

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

mein DAO sieht so aus:

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

Wenn ich database.CountriesDao().addCountries(countryModel); anrufe, erhalte ich den folgenden Raum-DB-Kompilierfehler: Fehler: (58, 31) Fehler: Kann nicht herausfinden, wie dieses Feld in der Datenbank gespeichert wird. Sie können in Betracht ziehen, einen Typkonverter dafür hinzuzufügen.

sollte es eine andere Tabelle geben, die CountryLang heißt? und wenn ja, wie soll der Raum angegeben werden, um sie auf Anweisung einfügen zu verbinden?

Das CountryLang-Objekt selbst sieht folgendermaßen aus:

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

}

die Antwort sieht so aus:

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

Für jedes Land wird es also nicht mehr als ein Element sein. Ich kann es bequem für nur einen Eintrag in der Liste country_lang anzeigen. Ich kann also einfach eine Tabelle für country_lang erstellen und dann wie sie mit CountryModel verlinkt wird. aber wie ? Kann ich einen Fremdschlüssel verwenden? Ich hatte gehofft, ich musste keine flache Datei verwenden. also muss ich sagen, dass ich es als json speichern muss? Wird empfohlen, keinen Raum für temporäre Zwecke zu verwenden? was stattdessen verwenden?

19
j2emanue

Wie Omkar sagte, kannst du nicht. Hier beschreibe ich, warum Sie @Ignore Annotation immer gemäß der Dokumentation verwenden sollten: https://developer.Android.com/training/data-storage/room/referencing-data.html#understand-no-object-references

Sie behandeln das Country-Objekt in einer Tabelle, um nur die Daten seiner Kompetenz abzurufen. Die Languages-Objekte werden in eine andere Tabelle verschoben, Sie können jedoch dasselbe Dao beibehalten:

  • Länder- und Sprachobjekte sind unabhängig. Definieren Sie einfach den primaryKey mit mehr Feldern in der Sprachentität (countryId, languageId). Sie können sie in Reihe in der Repository-Klasse speichern, wenn der aktive Thread der Worker-Thread ist: zwei Anforderungen für Einfügungen an das Dao.
  • Um das Länderobjekt zu laden, haben Sie die countryId.
  • Um die zugehörigen Sprachobjekte zu laden, verfügen Sie bereits über die countryId. Sie müssen jedoch warten, dass das Land zuerst geladen wird, bevor Sie die Sprachen laden, damit Sie sie im übergeordneten Objekt festlegen und nur das übergeordnete Objekt zurückgeben können.
  • In der Repository-Klasse können Sie dies wahrscheinlich in Reihe tun, wenn Sie das Land laden. Sie werden also Land und Sprachen synchron laden, wie Sie es auf der Serverseite tun würden! (ohne ORM-Bibliotheken).
10
Davideas

Sie können die Klasse einfach mit dem Listenobjektfeld einfügen, indem Sie TypeConverter und GSON verwenden.

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

Fügen Sie @TypeConverters Annotation vor Ihrem Listenobjektfeld hinzu.

@Entity(tableName = TABLE_NAME)
public class CountryModel {

//....

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

Weitere Informationen zu TypeConverters in Room finden Sie in unserem Blog hier .

38

Du kannst nicht.

Die einzige Möglichkeit, dies zu erreichen, ist die Verwendung von @ForeignKey constraint. Wenn Sie die Objektliste weiterhin in Ihrem übergeordneten POJO behalten möchten, müssen Sie @Ignore verwenden oder einen @TypeConverter angeben.

Für weitere Informationen folgen Sie diesem Blogbeitrag: -

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

und Beispielcode: -

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

5
Omkar Amberkar

Hier ist der Konverter von Aman Gupta in Kotlin für faule Googler, die das Kopieren von Textbändern lieben:

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

Fügen Sie @Embedded für das benutzerdefinierte Objektfeld hinzu (siehe zB)

//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