Android Persistence Room: "Cannot Figure Out How to Read This Field from a Cursor"

Android Persistence room: Cannot figure out how to read this field from a cursor

Document is really confusing. Try with just below classes:

1) User Entity:

@Entity
public class User {
@PrimaryKey
public int id; // User id
}

2) Pet Entity:

@Entity
public class Pet {
@PrimaryKey
public int id; // Pet id
public int userId; // User id
public String name;
}

enter image description here

3) UserWithPets POJO:

// Note: No annotation required at this class definition.
public class UserWithPets {
@Embedded
public User user;

@Relation(parentColumn = "id", entityColumn = "userId", entity = Pet.class)
public List<Pet> pets; // or use simply 'List pets;'

/* Alternatively you can use projection to fetch a specific column (i.e. only name of the pets) from related Pet table. You can uncomment and try below;

@Relation(parentColumn = "id", entityColumn = "userId", entity = Pet.class, projection = "name")
public List<String> pets;
*/
}
  • parentColumn refers to Embedded User table's id column,
  • entityColumn refers to Pet table's userId (User - Pet relation) column,
  • entity refers to table(Pet) which has relation with User table.

4) UserDao Dao:

@Dao
public interface UserDao {
@Query("SELECT * FROM User")
public List<UserWithPets> loadUsersWithPets();
}

Now try loadUsersWithPets(), which returns the users with their list of pets.

Edit: See my other answer for many ot many relation.

Android room persistent library - TypeConverter error of error: Cannot figure out how to save field to database

This is a common problem I've seen since Room was announced. Room does not support the ability to store Lists directly, nor the ability to convert to/from Lists. It supports converting and storing POJO's.

In this case the solution is simple. Instead of storing a List<CountryLang> you want to store CountryLangs (note the 's')

I've done a quick example of a solution here :

public class CountryLangs {
private List<String> countryLangs;

public CountryLangs(List<String> countryLangs) {
this.countryLangs = countryLangs;
}

public List<String> getCountryLangs() {
return countryLangs;
}

public void setCountryLangs(List<String> countryLangs) {
this.countryLangs = countryLangs;
}
}

This POJO is an inversion of your previous object. It is an object that stores a list of languages. Instead of a list of objects that store your language.

public class LanguageConverter {
@TypeConverter
public CountryLangs storedStringToLanguages(String value) {
List<String> langs = Arrays.asList(value.split("\\s*,\\s*"));
return new CountryLangs(langs);
}

@TypeConverter
public String languagesToStoredString(CountryLangs cl) {
String value = "";

for (String lang :cl.getCountryLangs())
value += lang + ",";

return value;
}
}

This converter takes a list of strings and converts them into a comma seperated string to be stored in a single column. When it fetches the string from the SQLite db to convert back, it splits the list on commas, and populates the CountryLangs.

Insure to update your RoomDatabase version after making these changes.You have the rest of the configuration correct. Happy hunting with the rest of your Room persistence work.

Android room/TypeConverter issue while retriving list of objects

The issue was I was saving data in the wrong entity, wrong TypeConverters and as a result, I was using the wrong Entity class at the time of database creation.

Here are the necessary changes I had to make to store the list of objects:

Flickr data class

@Entity(tableName = "FlickerImage")
data class FlickrImage(
@PrimaryKey(autoGenerate = true)
val id: Int,
val title: String,
@TypeConverters(MediaConverter::class)
val media: Media)

TypeConvertors for Media class

class MediaConverter {

@TypeConverter
fun fromMediaToJson(stat: Media): String {
return Gson().toJson(stat)
}

/**
* Convert a json to a list of Images
*/
@TypeConverter
fun fromJsonToMedia(jsonImages: String): Media {
val type = object : TypeToken<Media>() {}.type
return Gson().fromJson<Media>(jsonImages, type)
}
}

DAO class

@Dao
interface ImagesDao {

@Query("select * from FlickerImage")
fun getImages(): LiveData<List<FlickrImage>>

Database class

@Database(entities = [FlickrImage::class], version = 1, exportSchema = false)
@TypeConverters(MediaConverter::class)
abstract class FlickrDatabase: RoomDatabase() {
abstract val imagesDao: ImagesDao
}

private lateinit var INSTANCE: FlickrDatabase

fun getDatabase(context: Context): FlickrDatabase{

synchronized(FlickrDatabase::class.java){
if(!::INSTANCE.isInitialized){
INSTANCE = Room.databaseBuilder(context,
FlickrDatabase::class.java,
"flickerImages").build()
}
}
return INSTANCE
}

@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertAll(images: List<FlickrImage>)

}

Android Room - error: Cannot figure out how to save this field into database

Date is exactly the example given in https://developer.android.com/training/data-storage/room/referencing-data.

For example, if we want to persist instances of Date, we can write the following TypeConverter to store the equivalent Unix timestamp in the database:

public class Converters {
@TypeConverter
public static Date fromTimestamp(Long value) {
return value == null ? null : new Date(value);
}
@TypeConverter
public static Long dateToTimestamp(Date date) {
return date == null ? null : date.getTime();
}
}

The preceding example defines 2 functions, one that converts a Date object to a Long object and another that performs the inverse conversion, from Long to Date. Since Room already knows how to persist Long objects, it can use this converter to persist values of type Date.

Next, you add the @TypeConverters annotation to the AppDatabase class so that Room can use the converter that you've defined for each entity and DAO in that AppDatabase:

AppDatabase.java

@Database(entities = {User.class}, version = 1)
@TypeConverters({Converters.class})
public abstract class AppDatabase extends RoomDatabase {
public abstract UserDao userDao();
}

A side note: java.util.Date is considered to be badly designed (and java.util.Calendar is much worse). If you have any non-trivial date-time logic and can get away with API level 26 (Java 8 on desktop), it's generally better to use java.time package. And if you can't, see https://github.com/JakeWharton/ThreeTenABP for a backport.



Related Topics



Leave a reply



Submit