Polymorphism in Android Room when extending a non-Entity class - android

My server is sending me a list of object type Parent, as List<Parent> parents. I need to save this list in Room and to do so I need a PrimaryKey. So what I try to do in room is create something like
#Entity
class Child(mApple:String,mOrange:String):Parent(mApple,mOrange){
#PrimaryKeyy(autoGenerate=true)
var id:Int=0
}
And here is what parent looks like:
//it's a java class and so to be complete I will recreate it in Java
class Parent{
private final String mApple;
private final String mOrange;
private final String mDerivative;
public Parent(String apple, String orange){
mApple=apple;
mOrange=orange;
mDerivation = someMagic(apple,orange)
}
//then a bunch of methods for dealing with the state variables
}
The reason I want to extend Child from Parent is because it makes working with both Child and Parent together easy. But room is not compiling and I suspect it has to do with this. Unfortunately, Kotlin is not pointing me to where the error is occuring.
Is the error really due to the fact that Parent is not an Entity and that derivative does not provide a setter?
How do I get Android Studio to show me more details about this bug, such as line number? (linked to related questions)
The only errors I am able to get from the build tab:
error: Cannot figure out how to save this field into database. You can consider adding a type converter for it.
error: Cannot find getter for field.
error: Cannot find setter for field.
error: Cannot figure out how to read this field from a cursor.
error: Cannot find getter for field.
error: Cannot find setter for field.

Related

How does differentiate between Serialized data class and normal Serialized class?

While writing code for RecyclerView to get data I figured out there's a data class in Kotlin.
Following codes are taken from two different projects which are linked above.
#Serializable
data class MarsPhoto(
val id: String,
#SerialName(value = "img_src")
val imgSrc: String
)
class Contacts {
#SerializedName("country")
private val country:String? = null
fun getCountry():String?{
return country
}
}
I know that both classes are doing same job. So what does differentiate them? I also wonder in the MarsPhoto data class how they can get the id without declaring SerialName just the way they did for imgSrc. (I am just on the way to learning kotlin now, so I'm absolute beginner).
Basically for "data" class the compiler automatically derives the following members from all properties declared in the primary constructor:
equals()/hashCode() pair
toString() of the form "MarsPhoto(id=1, imgSrc=asdf)"
componentN() functions corresponding to the properties in their order of declaration.
copy()
You can read a lot more at enter link description here
On the SerializedName part of your question. if you are dealing with Gson lib by default it is using fields name as "SerializedName". And only if you want to use something different then field name, you can use SerializedName annotation and pass your custom value there. But usually, everybody just writes #SerializedName() with duplication of field names as value for every field.
It's a good idea if you are receiving and Serializing data from server from Json. Because Backend developers can use a bad keys in response, which you don't want to use in your code, so #SerializedName will be the only place where you will have to see this key, and you can name your fields however you like.
#Serializable used to mark class as serializable to disk or like into a file( alternative is Parcel able in android) special useful in case of process death or configuration changes and #SerializedName("country") used for json parsing when u receive the response from server
You get the id without #SerializedName because the JSON property field is the same as your variable name, but imgSrc and img_src is not. Still, even if they are the same, you should always use #SerializedName, because your variable names could be converted to random letters during code optimization, and obfuscation.

android Room with kotlin value class?

I'm trying to use a room entity with a value class:
#JvmInline
value class UserToken(val token: String)
and the entity:
#Entity(tableName = TABLE_AUTH_TOKEN)
data class TokenEntity(
#PrimaryKey val id: Int = 0,
val token: UserToken
)
I get the following 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).
public final class TokenEntity {
^
is it even possible to use room with value class? I couldn't find anything about this. thanks
See the comment from #CommonsWare. Android does not yet support value classes for Room.
The same holds true for the value classes introduced in kotlin 1.5. The type is not supported.
— Support Inline class in Room entity
Here is a possible explanation according to Kotlin Inline Classes in an Android World.
Looking to solve this you could try and add a TypeConverter for your Inline class, but since your Inline class is just the value it wraps when it’s compiled, this doesn’t make much sense and it doesn’t work as you’d expect even if you tried...
I’m just guessing it’s because this is a TypeConverter converting UserId to Int which is basically the same as Int to Int 😭. Someone will probably solve this problem, but if you have to create a TypeConverter for your Inline class then you are still plus one class for the count (multidex). 👎
I think yes if you can provide a type converter for it to change it to some sort of primitive data type (int , string, long ...etc) when it needs to be stored, and to change it back to its class type when it's fetched from database.
You can read about Type Converters from here
Referencing complex data using Room
other than that, your other class should be an entity and bind both your entities together using a Relation.
at least that's what I know about how to use Room.
UserToken always will have only one attribute? In this case, you don't need two classes, just use token: String directly on your entity class;
If you really need keep this class, you have two options:
TypeConverter, where you basically will convert the object into a json, and save as string in the database;
Relation, where you will transform the UserToken in a entity, and on TokenEntity save the tokenId.

Android Room Database Nested Object Reference for array of objects

I have a class structure with
Class x {
int a;
int b;
Y[] yList;
}
Class Y {
int m;
int n;
}
I am using data binding, Room and Dagger. I am getting compilation error as 'cannot find class DatabindingComponent', possibly because Room doesn't allow persistent of Nested objects. To enable I used #Embedded annotation but still getting same error. But if I use #Ignore annotation indicating Room that do not process this field; compilation is happening successfully.
How to reference nested array of objects for Room Database without foreign key?
Please refer to
https://developer.android.com/topic/libraries/architecture/room.html#no-object-references
https://developer.android.com/reference/android/arch/persistence/room/Ignore.html
https://developer.android.com/reference/android/arch/persistence/room/Embedded.html.
How to reference nested array of objects for Room Database without foreign key?
If you do not want Y to be an #Entity with its own table, the only option that I know of is to use #TypeConverters:
Define two static methods with #TypeConverter that convert Y[] to and from some basic type (e.g., String, by using JSON)
Register the class holding those methods using #TypeConverters somewhere (e.g., on your RoomDatabase subclass)
This works using collection classes (e.g., List, Set). I would guess that it works with Java arrays (Y[]), though I have not tried it.

Mock model object without setters

I'm implementing tests on my Android app and I want to do unit tests on a model. The problem is that I do not have setters as the model is created with Realm or by parsing a CSV file (witch univocity csv parser).
So, how could I create a mocked object with valid values? I have something like that:
public class Content {
private String title;
private String description;
...
}
How could I generate a mocked Content object with a title and description data?
Thanks in advance
Use code below in your test class:
Field field = Content.class.getDeclaredField("str");
field.setAccessible(true);
field.set(yourObject, "some value");
yourObject is a instance of Content that you use in your test class.
But you shouldn't fill mock object - you should just define method result for mock object.
A word of warning: reflection has a lot of disadvantages; for example a simple name change for your fields will go unnoticed; and not lead to compiler errors but to failing unit tests later on.
This I suggest a different solution - providing a package-private or protected constructor that you can use to initialize your fields. Then you do not to use reflection; and at the same time, your "public" interface of that class doesn't change either, like:
public class Content {
// unit testing only
Content(String title, ... ) { ...
But of course, you have to do balancing - either you add that constructor that isn't required for production; or you go with not so robust reflection code!

SimpleFramework and ORMLite - Foreign Collections - cannot instantiate

We are using ORMLite 4.41 in our Android app and facing this issue:
Having defined one column in class A:
#Element(required = false)
#ForeignCollectionField(eager=true, orderColumnName="Name",
columnName="TestItems")
public ForeignCollection<TestItem> Test
In class B we have defined foreign column:
#DatabaseField(foreign=true, foreignAutoRefresh=true,
columnName="TestItems")
public TestSummary TestItemId;
When we run it and it receives the input XML, Serializer.read method is called where the output should be classes filled with foreign keys being initialized.
Unfortunately, it always stops with the exception:
org.simpleframework.xml.core.InstantiationException: Cannot instantiate interface
com.j256.ormlite.dao.ForeignCollection for field 'Test' public
com.j256.ormlite.dao.ForeignCollection xxxxxxxx.TestModel$TestSummary.Test
Is there any way how to use ForeignCollection together with SimpleFramework?
Try using Collection instead, and I think you should be using ElementList instead of Element.
#ForeignCollectionField(eager=true, orderColumnName="Name",
columnName="TestItems")
#ElementList(required = false)
public Collection<TestItem> Test

Categories

Resources