Following SQLite query (with room database library) returning null result. Which is incorrect, as you can see in the attached image.
#Query("SELECT * FROM REPOSITORY_DATABASE_TABLE WHERE item_category LIKE :key")
fun getItemByCategory(key: String) : LiveData<List<Item>>?
#Query("SELECT * FROM REPOSITORY_DATABASE_TABLE WHERE item_storage_type = :key ORDER BY item_name")
fun getItemByCategories(key: String) : LiveData<List<Item>>?
where other queries as follow returning correct result in running application as well as in Android Debug Database
#Query("SELECT * FROM REPOSITORY_DATABASE_TABLE ORDER BY item_name")
fun getAllItems() : LiveData<List<Item>>
Item Data structure:
#Entity(tableName = ApplicationConstants.REPOSITORY_DATABASE_TABLE_NAME)
data class Item(
#PrimaryKey(autoGenerate = false) #SerializedName("item_id")
var itemID: Long = 0L,
#ColumnInfo(name = "item_guid") #SerializedName("item_guid")
var itemGUID: String = "0",
#ColumnInfo(name = "item_name") #SerializedName("item_name")
var itemName: String = "Mango",
#ColumnInfo(name = "item_category") #SerializedName("item_category")
var itemCategory: String = "Fruit",
#ColumnInfo(name = "item_weight") #SerializedName("item_weight")
var itemWeight: Int = 0,
#ColumnInfo(name = "item_count") #SerializedName("item_count")
var itemCount: Int = 1,
#ColumnInfo(name = "item_image") #SerializedName("item_image")
var itemImage: String = "https://stackoverflow.com/questions/62131564/a-failure-occurred-while-
executing-org-jetbrains-kotlin-gradle-internal-kaptexec",
#ColumnInfo(name = "item_notes") #SerializedName("item_notes")
var itemNotes: String = "Sweet Yellow Mango",
#ColumnInfo(name = "item_display_quantity") #SerializedName("item_display_quantity")
var itemDisplayQuantity: String = "0",
#ColumnInfo(name = "item_storage_type") #SerializedName("item_storage_type")
var itemStorageType: String = "0",
#ColumnInfo(name = "item_creation_date") #SerializedName("item_creation_date")
var itemCreationDate: String = "0",
#ColumnInfo(name = "item_is_checked") #SerializedName("item_is_checked")
var itemIsChecked: String = "0",
#ColumnInfo(name = "item_local_status") #SerializedName("item_local_status")
var itemLocalStatus: String = "0",
#ColumnInfo(name = "item_last_added") #SerializedName("item_last_added")
var itemLastAdded: String = "0",
#ColumnInfo(name = "item_notification_status") #SerializedName("item_notification_status")
var itemNotificationStatus: String = "0",
#ColumnInfo(name = "item_priority") #SerializedName("item_priority")
var itemPriority: String = "0",
#ColumnInfo(name = "item_notification_days") #SerializedName("item_notification_days")
var itemNotificationDays: Int = 1,
#ColumnInfo(name = "item_expiry") #SerializedName("item_expiry")
var itemExpiry: String = "0",
#ColumnInfo(name = "item_synonyms") #SerializedName("item_synonyms")
#TypeConverters(Converters::class)
var itemSynonyms: List<String> = listOf("0","0")
)
Also while debugging the database with Android Debug Database I am getting correct response as shown in following image
Android Debug Database Screenshot showing correct response
Try removing the null safety operator from the return type of the query like this:
fun getItemByCategory(key: String) : LiveData<List<Item>>
And you don't have to use #SerializedName() if the SerializedName name is going to be the same as the variable itself.
And also it is very recommended if you use #PrimaryKey(autoGenerate = True) instead of false. Otherwise, you have to manually update the PrimaryKey every time you modify your table.
Related
I'm trying to prepopulate a database, I generated the insert data but
when I run the app it gives this build error:
Caused by: org.jetbrains.org.objectweb.asm.MethodTooLargeException: Method too large:
I have a funtion like this:
RoomDatabase.Callback(){
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
val pDao = database.get().pDao()
val pLangDao = database.get().pLangDao()
applicationScope.launch {
insertPWithPLangEnglish_1_10(pDao, pLangDao)
}
}
}
private suspend fun insertPWithPLangEnglish_1_10(pDao: PDao, pLangDao: PLangDao){
var insId = pDao.insert(P(pcId = 1))
pLangDao.insert(PLang(pItemId = insId.toInt(), title = "herbert", locale = "en_US", langCode = "en"))
insId = pDao.insert(P(pcId = 1))
pLangDao.insert(PLang(pItemId = insId.toInt(), title = "others", locale = "en_US", langCode = "en"))
... and so on about 3000 more lines
}
Any idea how to solve this?
P class is the following:
#Entity(tableName = "p")
#Parcelize
data class P (
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "pid")
val pId: Int = 0,
#ColumnInfo(name = "pc_id")
val pcId: Int,
val created: Long = System.currentTimeMillis()
) : Parcelable {
}
PLang class is the following:
#Entity(tableName = "p_lang")
#Parcelize
data class PLang (
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "p_lang_id")
val pLangId: Int = 0,
#ColumnInfo(name = "p_item_id")
val pItemId: Int,
val locale: String = "",
#ColumnInfo(name = "lang_code")
val langCode: String,
val title: String,
val description: String = ""
) : Parcelable {
}
Now I'm trying another way with help of Workers, and seed database from json files.
So clearly, the Kotlin compiler is complaining because your method is too long and you should optimize it.
In order to do that you could create a global list containing all your PLang objects:
val pLangList = listOf(
PLang(title = "herbert", locale = "en_US", langCode = "en"),
PLang(title = "others", locale = "en_US", langCode = "en"),
...
)
Note that I didn't set the pItemId property, in fact, you should also change your PLang class, assigning a default value to it for convenience:
#Entity(tableName = "p_lang")
#Parcelize
data class PLang (
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "p_lang_id")
val pLangId: Int = 0,
#ColumnInfo(name = "p_item_id")
val pItemId: Int = -1, // <--- Added default value here
val locale: String = "",
#ColumnInfo(name = "lang_code")
val langCode: String,
val title: String,
val description: String = ""
) : Parcelable {
}
Now you can loop through your list and add every item with just 3 lines of code instead of ~3000:
private suspend fun insertPWithPLangEnglish_1_10(pDao: PDao, pLangDao: PLangDao)
{
for (pLang in pLangList)
{
val insId = pDao.insert(P(pcId = 1))
pLangDao.insert(PLang(pItemId = insId.toInt(), title = pLang.title, locale = pLang.locale, langCode = pLang.langCode))
}
}
Bear with me, it's a tricky question and what resources I've found around don't really help me resolve my problem.
I'm trying to build a real estate-oriented app on Kotlin. It must show at some point a RecyclerView with multiple object classes (say: houses, flats, plots, buildings, etc.)
I've seen multiple examples of RVs designed to accept multiple classes, but I'm struggling to put together a DB and the intermediary classes translating between tables and POJOs.
So far I've figured the following:
I must have a Properties table that stores the unique ID for every object, along with another identifier for its type and a series of values common to every property (say, address, price, etc.)
I must have a table for each entity type that can be independently listed as a real estate item (say, a house, a flat, a plot of land, a building, what have you). Each row on those tables will have a primary foreign key referencing its equivalent on the Properties table.
Now for the unexpected habanero. I decided to start sketching out my project on the basis of the RecyclerView Kotlin codelabs Google put together for newbies like me. Therein data is retrieved from the DB in this fashion:
this.plots = Transformations.map(database.RealtorDao.getPlots()) { it.asDomainModel() }
This works smoothly enough when the objects on the list the DB spits at you are all of one single kind, but what happens if you need them to be of different classes so that the adapter can tell them apart?
Or the only way around is just to build a gigantic table with about a hundred columns that will have nulls everywhere, and sort out objects ONLY AFTER they've been parsed in the previously described fashion?
I smashed my head against this wall until I got tired of hearing the squishing sound. I could not get a Room DB to return a list of objects of multiple classes, so I had to adopt a dirtier approach.
If I had worked just with the database classes then probably I could have hacked it, but trying to translate objects of such classes into POJOs to use instead complicated things somewhat.
The workaround I found was to make a master real estate class and accept that it would have lots and lots of null fields on the database. While a far cry from ideal, it works.
Database object classes:
open class DatabaseProperty
{
#ColumnInfo(name = COL_TYPE)
#SerializedName(COL_TYPE)
#Expose
var type: String? = null
#ColumnInfo(name = COL_ADDRESS)
#SerializedName(COL_ADDRESS)
#Expose
var address: String? = null
#ColumnInfo(name = COL_OWNER)
#SerializedName(COL_OWNER)
#Expose
var owner: String? = null
#ColumnInfo(name = COL_PRICE_FINAL)
#SerializedName(COL_PRICE_FINAL)
#Expose
var priceFinal: Long? = null
#ColumnInfo(name = COL_PRICE_QUOTED)
#SerializedName(COL_PRICE_QUOTED)
#Expose
var priceQuoted: Long? = null
/**
* No args constructor for use in serialization
*/
constructor()
#Ignore
constructor
(
type: String,
address: String,
owner: String,
priceFinal: Long,
priceQuoted: Long
) : super() {
this.type = type
this.address = address
this.owner = owner
this.priceFinal = priceFinal
this.priceQuoted = priceQuoted
}
}
#Entity
(
tableName = TABLE_RE,
indices =
[
Index(value = [COL_RE_ID], unique = true)
],
foreignKeys =
[
ForeignKey
(
entity = DatabaseRealEstate::class,
parentColumns = arrayOf(COL_RE_ID),
childColumns = arrayOf(COL_PARENT_ID),
onDelete = ForeignKey.NO_ACTION
)
]
)
data class DatabaseRealEstate
(
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = COL_RE_ID)
var id: Int? = null,
#ColumnInfo(name = COL_PARENT_ID)
var parent_id: Int? = null,
#Embedded(prefix = RE)
var property: DatabaseProperty? = null,
#ColumnInfo(name = COL_PARCEL_FRONT) // Plot front
#SerializedName(COL_PARCEL_FRONT)
#Expose
var front: Float? = null,
#ColumnInfo(name = COL_PARCEL_SIDE) // Plot side
#SerializedName(COL_PARCEL_SIDE)
#Expose
var side: Float? = null,
#ColumnInfo(name = COL_AREA) // Plot area
#SerializedName(COL_AREA)
#Expose
var area: Float? = null,
#ColumnInfo(name = COL_CATASTER)
#SerializedName(COL_CATASTER)
#Expose
var cataster: String? = null,
#ColumnInfo(name = COL_ZONIFICATION)
#SerializedName(COL_ZONIFICATION)
#Expose
var zonification: String? = null,
)
data class RealEstateWithSubunits
(
#Embedded
val re: DatabaseRealEstate? = null,
#Relation
(
parentColumn = COL_RE_ID,
entityColumn = COL_PARENT_ID,
entity = DatabaseRealEstate::class
)
var subunits: List<DatabaseRealEstate>? = null,
#Relation
(
parentColumn = COL_RE_ID,
entityColumn = COL_PARENT_ID,
entity = DatabaseChamber::class
)
var chambers: List<DatabaseChamber>? = null
)
fun List<RealEstateWithSubunits>.asRESUBDomainModel() : List<RealEstate>
{
return map { obj ->
RealEstate(
id = obj.re!!.id!!,
type = obj.re.property!!.type!!,
address = obj.re.property!!.address!!,
owner = obj.re.property!!.owner!!,
priceFinal = obj.re.property!!.priceFinal!!,
priceQuoted = obj.re.property!!.priceQuoted!!,
parent_id = obj.re.parent_id,
front = obj.re.front,
side = obj.re.side,
area = obj.re.area,
cataster = obj.re.cataster,
zonification = obj.re.zonification,
chambers = obj.chambers!!.asChamberDomainModel(),
subunits = obj.subunits!!.asREDomainModel()
)
}
}
fun List<DatabaseChamber>.asChamberDomainModel(): List<Chamber>
{
return map {
Chamber(
id = it.id,
parent_id = it.parent_id,
front = it.front,
side = it.side,
area = it.area
)
}
}
fun List<DatabaseRealEstate>.asREDomainModel(): List<RealEstate>
{
return map { obj ->
RealEstate(
id = obj.id!!,
type = obj.property!!.type!!,
address = obj.property!!.address!!,
owner = obj.property!!.owner!!,
priceFinal = obj.property!!.priceFinal!!,
priceQuoted = obj.property!!.priceQuoted!!,
parent_id = obj.parent_id,
front = obj.front,
side = obj.side,
area = obj.area,
cataster = obj.cataster,
zonification = obj.zonification,
chambers = ArrayList(),
subunits = ArrayList()
)
}
}
Model object classes:
interface BaseProperty {
var id: Int
var type: String
var address: String
var owner: String
var priceFinal: Long
var priceQuoted: Long
}
data class RealEstate(
override var id: Int = -1,
override var type: String = "",
override var address: String = "",
override var owner: String = "",
override var priceFinal: Long = 0,
override var priceQuoted: Long = 0,
var parent_id: Int?,
var front: Float?,
var side: Float?,
var area: Float?,
var cataster: String?,
var zonification: String?,
var subunits: List<RealEstate>? = null,
var chambers: List<Chamber>? = null
) : BaseProperty
{
fun hasParent() : Boolean
{
if (parent_id == null)
{
return false
}
return true
}
}
I haven't yet found a better approach, so if someone does, I'm welcoming it with open arms.
Can't see how to do this and getting rather confused!
I am saving 'site' objects to firestore, but I want to add a list of users associated to each site.
I have added a Map of users to my JSON object as below:
#IgnoreExtraProperties
data class SiteObject(
var siteReference: String,
var siteAddress: String,
var sitePhoneNumber: String,
var siteEmail: String,
var invoiceAddress: String,
var invoicePhoneNumber: String,
var invoiceEmail: String,
var website: String,
var companyNumber: String,
var vatNumber: String,
var recentProjectsText: String,
//not set up yet:
var sitePriority: Boolean,
var siteRating: Int,
var plusCode: String,
var users: Map<String, Boolean>?, // This is the map I have added
#ServerTimestamp
var dateCreatedTimestamp: Date?,
#ServerTimestamp
var dateEditedTimestamp: Date?,
#Exclude
var siteID: String?
) : Serializable {
private constructor() : this(
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
false,
1,
"",
null,
null,
null,
null
)
override fun toString(): String {
return "$siteReference"
}
}
And in my respository I am trying to add the current user to this list of users as below:
// save sites to firebase
fun saveSite(site: SiteObject) {
site.users?.plus(Pair(firebaseUser?.uid.toString(), true)) // This is where I expected the user Id to be added to Map of users..
val documentReference = firestore.collection("sites").document().set(site)
.addOnCompleteListener {
if(it.isSuccessful){
Log.d(TAG, "${site.toString()} saved")
lastOperationText.value = "New site, ${site.siteReference}, saved!"
} else {
Log.d(TAG, "${site.toString()} saved")
lastOperationText.value = "Save new site failed"
}
}
}
However, I still seeing null for users in the Firestore console.
Your code never gives an initial value to users. It starts off null. Since it doesn't get assigned a value, this code will not make a change to it, because it's first checking to see if users is null using the ?. operator:
site.users?.plus(Pair(firebaseUser?.uid.toString(), true))
You will need to assign it an initial value before trying to modify it. It should probably never be null and just start empty.
var users = HashMap<String, Boolean>()
For completeness, below is my updated data class. This initialises the values when it is created and also includes #Exclude #set:Exclude #get:Exclude on siteID to prevent this being saved to Firestore (used to store generated id when read from Firestore):
#IgnoreExtraProperties
data class SiteObject(
var siteReference: String = "",
var siteAddress: String = "",
var sitePhoneNumber: String = "",
var siteEmail: String = "",
var invoiceAddress: String = "",
var invoicePhoneNumber: String = "",
var invoiceEmail: String = "",
var website: String = "",
var companyNumber: String = "",
var vatNumber: String = "",
var recentProjectsText: String = "",
var sitePriority: Boolean = false,
var siteRating: Int = 1,
var plusCode: String = "",
var users: HashMap<String, Boolean> = hashMapOf(),
#ServerTimestamp
var dateCreatedTimestamp: Date? = null,
#ServerTimestamp
var dateEditedTimestamp: Date? = null,
#Exclude #set:Exclude #get:Exclude
var siteID: String = ""
) : Serializable {
override fun toString(): String {
return "$siteReference, $siteAddress, $siteRating, $siteID"
}
fun siteDetailsText(): String {
var siteDetailsText = siteAddress
if (sitePhoneNumber.isNotEmpty()) siteDetailsText.plus("\n\n$sitePhoneNumber")
if (website.isNotEmpty()) siteDetailsText.plus("\n\n$website")
return siteDetailsText
}
fun invoiceDetailsText(): String {
var invoiceDetailsText = invoiceAddress
if (invoicePhoneNumber.isNotEmpty()) invoiceDetailsText.plus("\n\n$invoicePhoneNumber")
if (companyNumber.isNotEmpty()) invoiceDetailsText.plus("\n\n$companyNumber")
if (vatNumber.isNotEmpty()) invoiceDetailsText.plus("\n\n$vatNumber")
return invoiceDetailsText
}
}
There are two tables, Task and StatisticsTask for Android room, StatisticsTask has foreign key taskId for Task.id. When I update StatisticsTask, how to tell ui query data again?
#Entity
#Parcelize
data class Task(
var name: String,
var important: Boolean = false,
var startTime: String = timeHm(),
var endTime: String = "",
var remind: String = "",
var repeat: String = "",
var repeatInterval: String = "",
var desc: String = "",
var endDate: String = "",
var startDate: String = nowOfString(),
#PrimaryKey(autoGenerate = true) var id: Int = 0
) : Parcelable {
#Ignore
var selected = false
#Ignore
var time: String? = null
#Ignore
var finished: Boolean = false
}
#Entity(foreignKeys = [ForeignKey(entity = Task::class, parentColumns = arrayOf("id"), childColumns = arrayOf("taskId"),
onDelete = CASCADE)])
#Parcelize
data class StatisticsTask(
var taskId: Int,
var date: Calendar?,
var finish: Boolean = false,
#PrimaryKey(autoGenerate = true)
var id: Int = 0
) : Parcelable {
}
//TaskViewModel.kt
fun finishTask(task: Task) {
launch {
statisticsTaskDao.updateById(task.id, !task.finished)
}
}
#Dao
interface StatisticsTaskDao {
#Query("update statisticstask set finish = :finish where taskId=:taskId and date = :date")
fun updateById(taskId: Int, finish: Boolean, date:Calendar = thisDayInitial())
}
//in my fragment
val model = ViewModelProviders.of(this)[TaskViewModel::class.java]
model.getAllOfToday().observe(this, Observer<List<Task>> {
//udpate data to adapter
})
//I want to finish this task here(in adapter)
iv_finished.setOnClickListener {
//task: first item in adapter
model.finishTask(task)
//This not work. when I finish task, how to update data again?
model.getAllOfToday()
}
This is for android-room 2.2.0-rc1, Android studio 3.5.
How to tell db query data again? I tried call this again is not working: model.getAllOfToday().observe(this#TodayFragment, Observer> {
dispatchData(it)
})
Iam using room database in kotlin via androidX library. When i download feed and try to save in database, crash occurs showing error sqlite:NOTNULL constraint failed.Json Feed has objects in some tags and also have null data in some tags.
But i did not add any not null properties in database. In another project iam using room database from arch library in java with same feed it actually works.
I tried like this with some seconday constructor
constructor(0, "", "", "", "", listOf<SubMenuApi>, 0)
and also this...
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import androidx.room.TypeConverters
#Entity(tableName = "tableMenu")
data class MenuApi(
#PrimaryKey(autoGenerate = true) var id: Int = 0,
#ColumnInfo(name = "title") #SerializedName("Title") var title: String = "",
#ColumnInfo(name = "TITLE_OF_ACCESS") #SerializedName("TitleofAccess") var titleOfAccess: String = "",
#ColumnInfo(name = "apexFileName") #SerializedName("AspxFileName") var apexFileName: String = "",
#ColumnInfo(name = "sectionId") #SerializedName("SectionId") var sectionId: String = "",
#TypeConverters(MenuConvertors::class) #SerializedName("SubMenu") var subMenuApi: List<SubMenuApi> = listOf<SubMenuApi>(),
#ColumnInfo(name = "subSecCount") #SerializedName("subsec_count") var subSecCount: Int = 0)
my database class
#Database(entities = [(MenuApi::class),], version = 1, exportSchema = false)
#TypeConverters(MenuConvertors::class)
abstract class AppDatabase : RoomDatabase() {
abstract fun dbDao(): DbDao
}
menu convertors
class MenuConvertors {
private val gson = Gson()
#TypeConverter
fun stringToList(data: String?): List<SubMenuApi> {
if (data == null) {
return emptyList()
}
val listType = object : TypeToken<List<SubMenuApi>>() {}.type
return gson.fromJson(data, listType)
}
#TypeConverter
fun listToString(subMenuApiList: List<SubMenuApi>): String {
return gson.toJson(subMenuApiList)
}
}
submenu
data class SubMenuApi(
#SerializedName("Title") var title: String? = "",
#SerializedName("TitleofAccess") var titleOfAccess: String? = "",
#SerializedName("AspxFileName") var apexFileName: String? = "",
#SerializedName("SectionId") var sectionId: String? = "",
#SerializedName("URL") var url: String? = "")
my error is
android.database.sqlite.SQLiteConstraintException: NOT NULL constraint failed: tableMenu.apexFileName (code 1299)
#################################################################
Error Code : 1299 (SQLITE_CONSTRAINT_NOTNULL)
Caused By : Abort due to constraint violation.
(NOT NULL constraint failed: tableMenu.apexFileName (code 1299))
#################################################################
at android.database.sqlite.SQLiteConnection.nativeExecuteForLastInsertedRowId(Native Method)
at android.database.sqlite.SQLiteConnection.executeForLastInsertedRowId(SQLiteConnection.java:915)
at android.database.sqlite.SQLiteSession.executeForLastInsertedRowId(SQLiteSession.java:788)
at android.database.sqlite.SQLiteStatement.executeInsert(SQLiteStatement.java:86)
at androidx.sqlite.db.framework.FrameworkSQLiteStatement.executeInsert(FrameworkSQLiteStatement.java:51)
at androidx.room.EntityInsertionAdapter.insert(EntityInsertionAdapter.java:97)
at com.thanthi.dtnext.dtnextapplication.database.DbDao_Impl.insertMenuData(DbDao_Impl.java:432)
at com.thanthi.dtnext.dtnextapplication.viewmodel.MenuViewModel$loadMenuData$1.onResponse(MenuViewModel.kt:53)
at retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall$1$1.run(ExecutorCallAdapterFactory.java:70)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:7325)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)
null tag in apexFileName
is there any mistake in my code or bug in androidX.room
Since you are using kotlin, the variables have to be mutable if they are can be null.
Add a ? after the variable types in your entity and that will fix it. Without ? you are basically guaranteeing that it won't be null.
#PrimaryKey(autoGenerate = true) var id: Int? = 0, #ColumnInfo(name = "title")
#SerializedName("Title") var title: String? = "",
#ColumnInfo(name = "TITLE_OF_ACCESS") #SerializedName("TitleofAccess") var titleOfAccess: String? = "",
#ColumnInfo(name = "apexFileName") #SerializedName("AspxFileName") var apexFileName: String? = "",
#ColumnInfo(name = "sectionId") #SerializedName("SectionId") var sectionId: String? = "",
#TypeConverters(MenuConvertors::class) #SerializedName("SubMenu") var subMenuApi: List<SubMenuApi>? = listOf<SubMenuApi>(),
#ColumnInfo(name = "subSecCount") #SerializedName("subsec_count") var subSecCount: Int? = 0)
After adding null in string initialization code works correctly.. like this
#ColumnInfo(name = "title") #SerializedName("Title") var title: String? = null
when we initialize variable with empty value, room database marks that column in table as NOT NULL, so when we receive data as null, it goes error.
so when we don't know about the value we have to initialize variable as null in kotlin data class
Did you change the database scheme after creation? => uninstall app and rebuild
Also you should set exportSchema = true so you actually see what the generated schema looks like.