library - sample room android
Biblioteca persistente de la sala Android: cómo insertar una clase que tiene un campo Objeto de lista (5)
En la biblioteca persistente de la sala Android, cómo insertar todo el objeto Modelo en la tabla, que tiene en sí misma otra lista.
Déjame mostrarte lo que quiero decir:
@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;
}
}
mi DAO se ve así:
@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);
}
Cuando llamo database.CountriesDao().addCountries(countryModel);
Obtengo el siguiente error de compilación de la base de datos de la sala: Error: (58, 31) error: No puedo averiguar cómo guardar este campo en la base de datos. Puedes considerar agregar un convertidor de tipos para ello.
¿Debería haber otra tabla llamada CountryLang? y si es así, ¿cómo decirle a la sala que los conecte en la declaración de inserción?
El objeto CountryLang se parece a esto:
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 respuesta se ve así:
"country_lang": [
{
"id_country": 2,
"id_lang": 1,
"name": "Austria"
}
]
Para cada país, por lo que no va a ser más de un elemento aquí. Me siento cómodo al diseñarlo para un solo elemento de la lista country_lang. Así que solo puedo hacer una tabla para country_lang y luego algunas formas de vincularla a CountryModel. pero cómo ? ¿Puedo usar clave externa? Esperaba no tener que usar un archivo plano. ¿Entonces tu diciendo que tengo que guardarlo como json? ¿Se recomienda no usar espacio para temporales? ¿Qué usar en su lugar?
Agregue @Embedded para el campo de objeto personalizado (refiérase a continuación, por ejemplo)
//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 {
....
}
Aquí está el convertidor de Aman Gupta en Kotlin para los holgazanes perezosos que disfrutan de pegar copias:
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)
}
}
Como dijo Omkar, no puedes. Aquí, describo por qué siempre debe usar la anotación @Ignore
acuerdo con la documentación: https://developer.android.com/training/data-storage/room/referencing-data.html#understand-no-object-references
Usted tratará el objeto País en una tabla para recuperar los datos de su competencia solamente; Los objetos de Idiomas irán a otra tabla, pero puedes mantener el mismo Dao:
- Los objetos de Países e Idiomas son independientes, simplemente defina la Clave principal con más campos en la entidad de Idioma (countryId, languageId). Puede guardarlos en serie en la clase Repositorio cuando el subproceso activo es el subproceso de trabajo: dos solicitudes de inserciones al Dao.
- Para cargar el objeto Países tienes el countryId.
- Para cargar los objetos de Idiomas relacionados, ya tiene el ID de país, pero deberá esperar a que ese país se cargue primero, antes de cargar los idiomas, para poder establecerlos en el objeto principal y devolver solo el objeto principal.
- Probablemente pueda hacer esto en serie en la clase Repositorio cuando cargue el país, por lo que cargará el país y luego los idiomas de forma síncrona, ¡como lo haría en el lado del Servidor! (sin bibliotecas ORM).
No se puede.
La única forma de lograr esto es usar la restricción @ForeignKey . Si aún desea mantener la lista de objetos dentro de su POJO principal, debe usar @Ignore o proporcionar un @TypeConverter
Para más información, siga esta entrada de blog: -
https://www.bignerdranch.com/blog/room-data-storage-for-everyone/
y código de muestra: -
https://github.com/googlesamples/android-architecture-components
Puede insertar fácilmente la clase con el campo de objeto de lista utilizando TypeConverter y 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;
}
}
Y añada @TypeConverters
Annotation antes de su campo de objeto de lista,
@Entity(tableName = TABLE_NAME)
public class CountryModel {
//....
@TypeConverters(DataConverter.class)
public List<CountryLang> getCountryLang() {
return countryLang;
}
//....
}
Para obtener más información acerca de TypeConverters in Room, consulte nuestro blog here .