I want to update a RealmObject that contains RealmList and I faced this error : (look at the images)
So why I faced this error? and what's the solution?
Also why RealmObject has duplicate parameters (image 2)
My code and the error are below
InvoiceModel :
open class PurchaseDatabase : RealmObject {
#PrimaryKey
var id: Int? = 0
var date: String? = ""
var supplier: String? = ""
var products: RealmList<ProductBuyModel> = realmListOf()
var totalPrice: Int? = 0
var paymentMethod: String? = ""
var paymentPrice: Int? = 0
var restPrice: Int? = 0
}
Error :
FATAL EXCEPTION: main
Process: com.bakirdev.gestiondestock, PID: 9705
java.lang.IllegalArgumentException: Cannot import an outdated object. Use findLatest(object) to find an
up-to-date version of the object in the given context before importing it.
at io.realm.kotlin.internal.RealmObjectListOperator.insert(RealmListInternal.k:370)
at io.realm.kotlin.internal.ListOperator$DefaultImpls.insertAll(RealmListInternal.kt:202)
at io.realm.kotlin.internal.BaseRealmObjectListOperator.insertAll(RealmListInternal.kt:258)
at com.bakirdev.gestiondestock.ui.purchases.PurchaseDatabase.setProducts(PurchaseDatabase.kt:150)
at com.bakirdev.gestiondestock.ui.purchases.AddNewInvoice$initListenerEdit$2$1.invoke(AddNewInvoice.kt:168)
at com.bakirdev.gestiondestock.ui.purchases.AddNewInvoice$initListenerEdit$2$1.invoke(AddNewInvoice.kt:162)
at io.realm.kotlin.internal.SuspendableWriter$write$2.invokeSuspend(SuspendableWriter.kt:109)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:236)
at android.os.HandlerThread.run(HandlerThread.java:67)
My Code :
var invoiceModel: PurchaseDatabase? = purchasesList[position]
realm.writeBlocking {
// fetch a frog from the realm by primary key
invoiceModel = this.query<PurchaseDatabase>("id == ${model.id}").first().find()
// modify the frog's age in the write transaction to persist the new age to the realm
invoiceModel?.supplier = purchasesSupplier
invoiceModel?.products?.clear()
invoiceModel?.products = products
invoiceModel?.totalPrice = totalPrice
invoiceModel?.paymentMethod = paymentMethod
invoiceModel?.paymentPrice = purchasesPaymentPrice.trim().toInt()
invoiceModel?.restPrice = purchasesRestPrice.trim().toInt()
}
Purchases.invoiceModel = invoiceModel
val returnedValue = Intent().apply {
putExtra("position", position)
}
setResult(RESULT_OK, returnedValue)
finish()
This is my model class
#Parcel
data class ClientModel(
var name: String? = "",
var phone: String? = "",
var princpalAddresse: String? = "",
var homeAddresse: String? = "",
var travailleAddresse: String? = "",
var email: String? = "",
var userToken: String? = "",
var principalAddresseCoords: Pair<Double, Double>? = null,
var homeAddresseCoords: Pair<Double, Double>?= null,
var workAddresseCoords: Pair<Double, Double>? = null,
)
My proGuard file keep the class :
-keep class com.olivier.oplivre.models.ClientModel
But! when I try to get the snapshot with a singleValueEventListener I got exception because of the Pair<Double,Double> variables
val utilisationInfo = snapshot.getValue(ClientModel::class.java) //todo CRASH
Exception :
com.google.firebase.database.DatabaseException: Class kotlin.Pair does not define a no-argument constructor. If you are using ProGuard, make sure these constructors are not stripped.
Database Structure :
I think firebase Realtime database treat your principalAddresseCoords as a list of long so in your ClientModel change the value of principalAddresseCoords to emptyList() and the type List
As #Sami Shorman said , firebase took my Pair instance and transform it but not as list, as Hashmap<,> ! so I changed my class model like that :
var principalAddresseCoords: HashMap<String,Double>? = null,
var homeAddresseCoords: HashMap<String,Double >? = null,
var workAddresseCoords: HashMap<String,Double >? = null,
To put the data as Hashmap I just had to do :
clientModel.workAddresseCoords = HashMap<String,Double>().apply {
put("lat",lat)
put("long",long)
}
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.
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.
I m having a databaseView code like this.
#DatabaseView("SELECT * FROM Product")
data class ProductDatabaseView(
var productCode: String? = "",
var productName: String? = "",
var longDescription: String? = "",
var productUom: String? = "",
var customerCode: String? = "",
var price: String? = ""
)
And I wanna pass some value as we do in our DAO like this.
#DatabaseView("SELECT * FROM Product WHERE priceListCode = :priceListCode")
data class ProductDatabaseView(
var productCode: String? = "",
var productName: String? = "",
var longDescription: String? = "",
var productUom: String? = "",
var customerCode: String? = "",
var price: String? = ""
)
Is it able to pass value like this or is there any other option available with the help of View.
To my knowledge, no you can't pass argument to #DatabaseView. They work the same way as in sqlite which doesn't allow parameters in views.