Kotlin and ObjectBox: Relations in Data Classes - android

How can I initialize a Data Class with a ToOne relation? For example, I have the two Data Classes below:
#Entity
data class EntityA(
#Id var id: Long,
var entityB: ToOne<EntityB>
)
#Entity
data class EntityB(
#Id var id: Long
)
Now, I want to initialize EntityA like that:
var e = EntityA(1, EntityB())
But, obviously, I can't do that because the second argument is a ToOne and not an EntityB. I tried to instantiate ToOne but its constructor wants a second argument that I don't know how to create.

Do not put the relation in the primary constructor. Then you can use a secondary constructor to call toOne.target = entity. It should look like something like this:
#Entity
data class EntityA(#Id var id: Long) {
lateinit var entityB: ToOne<EntityB>
constructor(b: EntityB) : this(0) {
entityB.target = b
}
}

Related

How to get copy of the ArrayList<Sealed Class> in android using kotlin

How to get the copy of arraylist of sealed class in android
private var homePageApiResponseList : ArrayList<HomeApiResponseModel> = ArrayList()
Here HomeApiResponseModel is a Sealed class. HomeApiResponseModel is given as Below
sealed class HomeApiResponseModel {
data class HomeCategoryListModel(
var categoryList : MutableList<CategoryModel> = mutableListOf(),
var categoryNameType : String = ""
) : HomeApiResponseModel()
data class HomeBestSellerListModel(
var bestSellerList : MutableList<ChildrenModel> = mutableListOf(),
var bestSellerNameType : String = ""
) : HomeApiResponseModel()
data class HomeMustTryListModel(
var mustTryList : MutableList<ChildrenModel> = mutableListOf(),
var mustTryNameType : String = ""
) : HomeApiResponseModel()
}
Normally arraylist of object copy is easly obtain by anyList.map { it.copy() }
While in sealed class it shows error. How to get a copy of arraylist of sealed class
Thanks
Create an abstract function in the parent. Each child can implement it and call through to their own copy(). The abstract function should have a different name than “copy” to avoid conflicts.
By the way, in your case, a sealed interface is probably a cleaner choice than a sealed class because there is no common functionality between the children. And I suggest avoiding combining mutable collections with var properties. Making something mutable in two different ways adds (usually unnecessary) complexity and more opportunities for bugs.

How to access fields of a Generic type in Kotlin?

I have a Generic class.
class GenericsClassExample<T>(
var data: T
)
I have another class which I'm passing in place of T.
data class MyDataClass1(val id: String, val value: String)
And I'm instantiating Generic Class like :
val genericClass = GenericClass<MyDataClass1>()
I would like to know how to access the fields (id and value) from genericClass object without knowing the type of the class I'm passing, i.e. I could also pass another class like MyDataClass1 say MyDataClass2 which also has its own fields.

Kotlin/Room/Android Error: Getting error while Querying for nested object which can have null fields

I am trying to request data from an API which responds with an array of nested JSON object in which some entries of the object can be null.
Each of the object has a structure similar to the following :
data class A(
#ColumnInfo("item1InA")
val item1InA:Double?=null,
#ColumnInfo("item2InA")
val item2InA:Double?=null,
)
data class B(
#ColumnInfo("item1InB")
val item1InB:Double?=null,
#ColumnInfo("item2InB")
val item2InB:Double?=null,
)
#Entity(tableName="temp_table")
data class Combination(
#PrimaryKey
#NonNull
#ColumnInfo("key")
val key:String='-',
#ColumnInfo("originalItem1")
val originalItem1:String='-',
#ColumnInfo("originalItem2")
val originalItem2:String='-',
#Embedded
val a:A=A(),
#Embedded
val b:B=B(),
)
I am querying the database in the application to get the list of the all Combination objects using the following interface :
#Dao
interface CombinationDao{
#Query("SELECT * FROM temp_table")
suspend fun getAll():List<Combination>
}
The issue is that i am getting the error: java.lang.NullPointerException: Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkNotNullParameter
When I checked on the directed links to the CombinationDao_Impl.java file, I got the following error: both methods have same erasure, yet neither overrides the other
I don't understand what I am doing wrong here. Can anyone help me get on the right track?
Sample JSON
{
"A":{
"item1InA":23.0,
"item2InA":null,
},
"B":{
"item1InB":67327.309,
"item2InB":null,
},
"key":"thisIsUnique",
"originalItem1":"hello",
"originalItem2":"world",
}
According to the documentation at Embedded | Android Developers,
When an embedded field is read, if all fields of the embedded field (and its sub fields) are null in the Cursor, it is set to null. Otherwise, it is constructed
The issue with my code was that for some JSON objects each of the fields of the embedded objects were null causing room to simply ignore to construct the object making the variables a and b to point at null.
Changing the code of the entity class to,
data class Combination(
#PrimaryKey
#NonNull
#ColumnInfo("key")
val key:String='-',
#ColumnInfo("originalItem1")
val originalItem1:String='-',
#ColumnInfo("originalItem2")
val originalItem2:String='-',
#Embedded
val a:A?=A(),
#Embedded
val b:B?=B(),
)
solved the issue for me.

Serialization with GSON in a single Entity class

I have a JSON with a parent called "posts" and I have the respective children. I am using GSON to serialize the fields into my entity(I'am using Room).
I am using a POJO called Post like
#Entity
data class Post(
#SerializableName("somethig") val something:Int
#SerializableName("somethig") val something:Int
)
And I am using a class with a List of it
class PostParent{
#Serializablename("posts") listPosts : List<Post> //to call the parent
}
Is there any other way to pass the parent JSON name directly in the Entity class instead of doing a class with a single List? I want to avoid this because I have a dao where I need to insert and fetch the data and he only accepts Lists, so I cant have my dao method return the class
Method
override fun loadFromDb(): LiveData<PostParent> { //error in build time
return postsDao.getPosts() //tried to do postsDao.getPosts().data already
}
Try doing this. Have not tried it myself yet, so you might need to edit some syntax here n there.
#Entity
class PostParent{
#Serializablename("posts") listPosts : List<#Embedded Post>
}
data class Post(
#SerializableName("somethig") val something:Int
#SerializableName("somethig") val something:Int
)

Room: related entities - usable public constructor

To get a OneToMany relation with Room I create a POJO with #Embedded object and #Relation variable.
data class SubjectView(
#Embedded
var subject: Subject,
#Relation(parentColumn = "idWeb", entityColumn = "subject_id", entity = Topic::class)
var topics: List<Topic>?
)
But while compiling I have this error
error: Entities and Pojos must have a usable public constructor. You can have an empty constructor or a constructor whose parameters match the fields (by name and type)
[...]
Tried the following constructors but they failed to match:
SubjectView(biz.eventually.atpl.data.db.Subject,java.util.List<biz.eventually.atpl.data.db.Topic>) : [subject : subject, topics : null]
Well, that constructor [subject : subject, topics : null] looks like the good one ???
However, if I change my class with no-arg constructor and an all params constructor, it does work.
class SubjectView() {
#Embedded
var subject: Subject = Subject(-1, -1, "")
#Relation(parentColumn = "idWeb", entityColumn = "subject_id", entity = Topic::class)
var topics: List<Topic>? = null
constructor(subject: Subject, topics: List<Topic>?) : this() {
this.subject = subject
this.topics = topics
}
}
I would like to know why the first (quicker) version does not compile, as it is not as the documentation shows.
Default args for all variables (as I could have seen on other post) in a constructor (data) class seems not to be mandatory though?
Thanks
There are several topics how data class generate the constructors.
Since you have a nullable Object inside your constructor, it will generate all possible constructors. That means it generates
constructor(var subject: Subject)
constructor(var subject: Subject, var topics: List<Topic>)
There are two ways to solve that. The first one is to predefine all values like and create another ignored constructor with the desired constructor.
data class SubjectView(
#Embedded
var subject: Subject,
#Relation(parentColumn = "idWeb", entityColumn = "subject_id", entity = Topic::class)
var topics: List<Topic> = ArrayList()
) {
#Ignore constructor(var subject: Subject) : this(subject, ArrayList())
}
Another way is creating a half-filled data class like
data class SubjectView(#Embedded var subject: Subject) {
#Relation var topics: List<Topic> = ArrayList()
}
Take care that the first solution is the proper solution and you need to set #Ignore to any other constructor.

Categories

Resources