I am using Room Database to store data in my application. But, trying to compile the project, I get the following error: "Invalid query argument". I tried to find the reason in the official documentation and also googling cause of this error, but it was all to no avail. Please, could you help by explaining what this error is in general, what causes it and what I need to fix in the project?
Exact error message
C:\Users\***\ParsedDatabase.java:10: error: Invalid query argument: int. It must be a class or an interface.
public final class ParsedDatabase {
^
Model - ParsedDatabase (Room Database entity, class with problem)
#Entity(tableName = "parsed_database")
data class ParsedDatabase(
#PrimaryKey()
val id: Int = 1,
#ColumnInfo(name = "app_config")
val appConfigSection: HashMap<String, String>,
#ColumnInfo(name = "loans")
val loansList: ArrayList<Offer>,
#ColumnInfo(name = "cards")
val cardsList: ArrayList<ArrayList<Offer>>,
#ColumnInfo(name = "credits")
val creditsList: ArrayList<Offer>,
) {
enum class AppConfigKeys {
app_config,
user_term_html,
extra_field_,
}
enum class LoansAndCreditsKeys {
loans,
credits,
}
enum class CardsKeys {
cards,
}
}
Dao class (I attach just in case)
#Dao
interface LocalDatabaseDao {
#Insert(onConflict = REPLACE)
fun save(parsedDatabase: ParsedDatabase)
#Query("SELECT * FROM parsed_database WHERE id = :id")
fun load(id: Int = 1): ParsedDatabase?
#Delete
fun delete(parsedDatabase: ParsedDatabase)
#Delete(entity = ParsedDatabase::class)
fun delete(id: Int = 1)
}
Database class
#Database(entities = [ParsedDatabase::class], version = 1, exportSchema = false)
#TypeConverters(Converters::class)
abstract class LocalDatabase : RoomDatabase() {
abstract fun getLocalDatabaseDao(): LocalDatabaseDao
}
#Delete(entity = ParsedDatabase::class)
fun delete(id: Int = 1)
I do not think that Room knows what to do with this. If you need that operation, you could try:
#Query("DELETE FROM parsed_database WHERE id = :id")
fun delete(id: Int = 1)
Related
I am getting the following error after deleting the RoomDB data:
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String app.zimablue.artbookfragmentversion.model.ArtEntity.getArtName()' on a null object reference
I think I am getting this error because I am trying to access the deleted data again, because I can delete the data, but I get an error because there is no data that will appear on the screen after it is deleted.
#Dao
interface ArtDao {
#Insert
suspend fun insert(artEntity: ArtEntity)
#Delete
suspend fun delete(artEntity: ArtEntity)
#Query("SELECT * FROM `art-table`")
fun getArtWithNameAndId():Flow<List<ArtEntity>>
#Query("SELECT * FROM `art-table` WHERE id = :id")
fun getArtById(id: Int): Flow<ArtEntity>
#Entity(tableName = "art-table")
class ArtEntity(
#ColumnInfo(name = "name")
#NonNull var artName : String,
#NonNull #ColumnInfo(name = "artistname")
var artistName: String,
#NonNull #ColumnInfo(name = "year")
var year : String,
#NonNull #ColumnInfo(name = "image")
var image : ByteArray
) {
#PrimaryKey(autoGenerate = true)
var id : Int = 0
}
the line where the error is pointed.
private fun oldArtDetails(art : ArtEntity){
artFromMain = art
art.artName.let {
binding.artText.setText(it)
}
art.artistName.let {
binding.artistText.setText(it)
}
art.year.let {
binding.yearText.setText(it)
}
art.image.let {
val bitmap = it.let {
BitmapFactory.decodeByteArray(it,0, it.size) }
binding.imageView.setImageBitmap(bitmap)
}
}
I have uploaded the version of the application where the error is occurring to Github:
https://github.com/mertparlak-zima/ArtBookFragment
Translation: I checked if "art.name" and "art.artistName" are null, but it didn't work. I made it nullable in the database, but it still didn't work.
In your ArtDao return type is set to non-nullable, so dao returns ArtEntity that actually null but it can't be checked that it is not null.
Solution:
#Query("SELECT * FROM `art-table` WHERE id = :id")
fun getArtById(id: Int): Flow<ArtEntity?>
It works fine when getting the list from Room but when i try to update it it gives me this error:
android.database.sqlite.SQLiteException: near "?": syntax error (code 1 SQLITE_ERROR): , while compiling: UPDATE reminderconfig SET alarmDays = ?,?,?,?,?,?,? WHERE id = 1
Here is the query:
#TypeConverters(Converters::class)
#Query("UPDATE reminderconfig SET alarmDays = :days WHERE id = 1")
suspend fun updateAlarmDays(days: List<Int>)
Here is my Converters class:
class Converters {
#TypeConverter
fun fromString(value: String): List<Int> {
val type = object: TypeToken<List<Int>>() {}.type
return Gson().fromJson(value, type)
}
#TypeConverter
fun fromArrayList(list: List<Int>): String {
val type = object: TypeToken<List<Int>>() {}.type
return Gson().toJson(list, type)
}
}
And here is the Database:
#Database(entities = [ReminderConfig::class], version = 1, exportSchema = false)
#TypeConverters(Converters::class)
abstract class ReminderDatabase : RoomDatabase() {
abstract fun reminderDao(): ReminderDao
}
Entity class:
#Entity
class ReminderConfig(
#PrimaryKey
val id: Int = 1,
#ColumnInfo
val timeInterval: Int = DEFAULT_INTERVAL_REPEATING,
#ColumnInfo
val startTime: Int = DEFAULT_START_TIME_PERIOD,
#ColumnInfo
val endTime: Int = DEFAULT_END_TIME_PERIOD,
#ColumnInfo
val alarmDays: List<Int> = listOf(0,1,2,3,4,5)
)
This was a bug and is fixed the latest Room Alpha release (2.3.0-alpha04).
Google Issue Tracker Link
I have list of custom object that i wanna save it in the database.
So I have to use TypeConverters to make this possible.
My problem that i get an error when I implement the functionality and I noticed
that a function annotated with TypeConverter never used
Here's the error:
A failure occurred while executing org.jetbrains.kotlin.gradle.internal.KaptExecution
error: The columns returned by the query does not have the fields [avatar,email,first_name,id,last_name] in com.hraa.fakeusers.model.UserData even though they are annotated as non-null or primitive. Columns returned by the query: [data]
public abstract androidx.lifecycle.LiveData<java.util.List<com.hraa.fakeusers.model.UserData>> getUsers();
And here's the code:
#Entity(tableName = USER_DATA_TABLE)
data class DataModel(
val data: List<UserData>,
val page: Int,
val per_page: Int,
val total: Int,
val total_pages: Int
) {
#PrimaryKey(autoGenerate = true)
var id: Int? = null
}
data class UserData(
val avatar: String,
val email: String,
val first_name: String,
val id: Int,
val last_name: String
)
class Converters {
#TypeConverter
fun toUsersData(value: String): List<UserData> {
val type = object : TypeToken<List<UserData>>() {}.type
return Gson().fromJson(value, type)
}
#TypeConverter
fun fromUsersData(usersData: List<UserData>): String {
return Gson().toJson(usersData)
}
}
#Database(entities = [DataModel::class], version = 1, exportSchema = false)
#TypeConverters(Converters::class)
abstract class AppDatabase: RoomDatabase() {
abstract fun dataDao(): DataDao
}
#Dao
interface DataDao {
#Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertData(data: DataModel)
#Delete
suspend fun deleteData(data: DataModel)
#Query("SELECT data FROM USER_DATA_TABLE")
fun getUsers(): LiveData<List<UserData>>
}
Note: toUsersData() function never used.. I don't know why.
Note: toUsersData() function never used
How can you be sure of that? My guess is that this function could work well, but you have two type's transformations here:
#Query("SELECT data FROM USER_DATA_TABLE")
fun getUsers(): LiveData<List<UserData>>
Transformation #1 (row level). Input: String (saved in db). Output: data (List).
That should be processed well thanks to your toUsersData() method (may be not, I've not checked, but it seems it should do)
Transformation #2 (row level). Input: data (List). Output: UserData (According to your desired return type). For that Room doesn't know how to do this transformation, so you have en error.
To check if your toUsersData() really works you can test next query:
#Query("SELECT * FROM USER_DATA_TABLE")
fun getUsers(): LiveData<List<DataModel>>
If your build is successful, then there is no problem with this function. You can also find this function in Java-class, that was autogenerated by Room during build.
You can try to add another data class:
data class UserDataList(
val data: List<UserData>
)
and change your data method to:
#Query("SELECT data FROM USER_DATA_TABLE")
fun getUsers(): LiveData<List<UserDataList>>
I am implementing a local cache using Room. I have created typeconverter to convert list of objects to json and back. But I am receiving mapping issue while retrieving data from json with error:
The columns returned by the query does not have the fields [title,media] in
com.example.theApp.data.FlickrImage even though they are annotated as non-null or
primitive. Columns returned by the query: [items]
Another one like this:
error: Cannot figure out how to read this field from a cursor.
private final com.example.theApp.data.Media media = null;
I tried other answers here but its not associated directly with this issue.
Here is my typeconverter:
class FlickrImageConverters {
#TypeConverter
fun fromImageListToJson(stat: List<FlickrImage>): String {
return Gson().toJson(stat)
}
/**
* Convert a json to a list of Images
*/
#TypeConverter
fun fromJsonToImagesList(jsonImages: String): List<FlickrImage> {
val type = object : TypeToken<List<FlickrImage>>() {}.type
return Gson().fromJson<List<FlickrImage>>(jsonImages, type)
}
}
Here is my entity class:
#Entity
data class DatabaseImagesEntity(
#PrimaryKey
#TypeConverters(FlickrImageConverters::class)
#SerializedName("item")
val items: List<FlickrImage>)
Dao class
#Dao
interface ImagesDao {
#Query("select * from DatabaseImagesEntity")
fun getImages(): List<FlickrImage>
#Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertAll(images: List<FlickrImage>)
}
FlickrImage class
data class FlickrImage(val title: String, val media: Media)
Media class
data class Media(val m: String)
LatestImage class
data class LatestImages(val items: List<FlickrImage>)
Please let me know if you faced this issue and if you know the solution for this.
Room database implementation
#Database(entities = [DatabaseImagesEntity::class], version = 1,
exportSchema = false)
#TypeConverters(FlickrImageConverters::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.applicationContext,
FlickrDatabase::class.java,
"flickerImages").build()
}
}
return INSTANCE
}
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>)
}
You need to add the appropriate annotations to your data class - e.g. for Gson you need to add the annotations #SerializedName("field_name") Otherwise, there's no way for the converters to know how to translate the json.
To clarify, the current annotations you have are only for Room. Just check with whatever json library you are using for the necessary logic.
#Entity(tableName = "images")
data class DatabaseImagesEntity(
#PrimaryKey(autoGenerate = true)
var id: Int? = 0,
#TypeConverters(FlickrImageConverters::class)
#SerializedName("item")
val items: MutableList<FlickrImage>? = null
)
or
#Entity(tableName = "images")
class DatabaseImagesEntity {
#PrimaryKey(autoGenerate = true)
var id: Int? = 0
#TypeConverters(FlickrImageConverters::class)
#SerializedName("item")
val items: MutableList<FlickrImage>? = null
}
then update your DAO query to #Query("select * from images")
I named it images as an example - you can choose whatever you want.
class ListConverter {
//from List to String
#TypeConverter
fun fromList(list : List<Object>): String {
return Gson().toJson(list)
}
// from String to List
#TypeConverter
fun toList(data : String) : List<Object> {
if (data == null){
return Collections.emptyList()
}
val typeToken = object : TypeToken<List<Object>>() {}.type
return Gson().fromJson(data,typeToken)
}
}
I'm trying to use Room with LiveData in my project. In my app, I have the authors table. The data is inserted fine but when I'm trying to read something from the table it did not give me records. I also see the database with SQLite Opener software. It shows me all the data.
Below is my Authors Entity.
#Entity(tableName = "authors")
data class AuthorModel(
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "id") val id: Long = 0,
#ColumnInfo(name = "name") var name: String
)
And this is my Author Dao interface.
#Dao
interface AuthorDao {
#Query(value = "SELECT * FROM authors")
fun allAuthors(): LiveData<List<AuthorModel>>
#Query(value = "SELECT * FROM authors WHERE id = :authorId")
fun authorWithId(authorId: Long): LiveData<AuthorModel?>
#Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(author: AuthorModel): Long
}
And last, this is my RoomDatabase class.
#Database(entities = [AuthorModel::class], version = 1)
abstract class BookLibraryDatabase : RoomDatabase() {
abstract fun authorDao(): AuthorDao
}
Thank you for your time.
You have to observe LiveData. livedata.value will be null when you get first time.