Android Room Database Nested Object Reference for array of objects - android

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.

Related

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.

Polymorphism in Android Room when extending a non-Entity class

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.

Android Room: #Ignore vs Transient

Are those two interchangable in context of Room database entity, or, if not, what are the differences between them?
#Ignore is a Room-specific annotation, saying that Room should ignore that field or method.
transient is a Java construct, indicating that this field should not be serialized in standard Java serialization. Room happens to treat this similarly to #Ignore by default. Mostly, that is there for cases where you are inheriting from some class that happens to use transient and you do not control that class (e.g., it is from a library).
For your own code, if you are not using Java serialization, I recommend sticking with #Ignore for the fields. transient is not an available keyword for a method, so to tell Room to ignore certain constructors, you have no choice but to use #Ignore.
Adding to CommonsWare's answer
transient is not good option for ignoring fields for Room as CommonsWare answered. It will create blocker when same modal is being used to parse data from server and store into database.
Let's assume you have a modal class MyModal.java as below
public static class MyModal {
#SerializedName(“intField”)
public int intField;
#SerializedName(“strField”)
public String strField;
#SerializedName(“booleanField”)
public boolean booleanField;
}
If you want to NOT SAVE booleanField into database, and if you modified that field as
transient : It will ignore this field while saving into database, BUT it will also ignore this field while parsing data which comes from server.
#Ignore : It will only ignore this field while inserting data into database, but this field will participate into json parsing.

Defining an ActiveAndroid model without creating a table

I have my app's networking code separated into a separate package. This package contains a POJO model that I'm serializing with Gson (using its #Expose annotation):
public class User {
#Expose
String username;
#Expose
String pathToAvatar;
// etc.
}
My app also is using ActiveAndroid as a database wrapper. I would like to extend the .networking.User model and add a couple more database-specific fields (secretKey or something) in my main logic. It would be nice if the networking code did not know its model was being used in a database. Unfortunately, I'm having to have .networking.User extend the ActiveAndroid Model base class (and having to label each networking variable with #Column). This works, with one problem: I'm getting a dummy User database table that corresponds with the networking code's class, even though it is not annotated with #Table, simply because it derives from Model. The main code's table is created correctly.
Can I prevent ActiveAndroid from creating a table when it finds a certain Model? Alternatively, is there a better way to approach this?

Persisting entity relationships from REST service for Android

I am trying to maintain a local store of a database, accessible via a REST API. I am attempting to use the fantastic RoboSpice and Spring libraries for the REST client, Jackson to parse/cache the JSON response and ORMLite to persist the resulting objects.
The problem is that I don't know how to store foreign object relationships for the JSON responses I get. Sometimes the JSON objects are nested, other times they are referenced by Id.
A typical systems response
{
id:567,
name:"The only system",
competitions:[{
id:123,
system_id:567
...
}];
}
A competitions response
items: {
123:{
id:123,
system_id:567 // System only referenced by id
...
}}
Another competitions response
items: {
123:{
id:123,
system_id:567, // System referenced by id and nested
system:{
id:567,
name:"The only system",
...
}
}
}
Nested objects work fine, but it's the cases where there is a reference by id that is killing it. I have two classes for my data model as follows.
Systems class
#DatabaseTable("systems")
public class System {
#JsonProperty("id")
#DatabaseField(id=true, columnName="id")
private long id;
#JsonProperty("name")
#DatabaseField(columnName="name")
private String name;
#JsonProperty("competitions")
#ForeignCollectionField
private ForeignCollection<Competition> competitions;
// getters & setters omitted
}
Competition class
#DatabaseTable("competitions")
public class Competition {
#JsonProperty("id")
#DatabaseField(id=true,columnName="id")
private long id;
#JsonProperty("system_id")
#DatabaseField(columnName="id")
private long systemId; // This is definitely at least part of the problem
#JsonProperty("system")
#DatabaseField(foreign=true, columnName="system_id")
private System system;
// getters & setters omitted
}
Having two properties references the system_id seems like a definitively bad idea, but I can't find an alternative for the behaviour I want. Even if the system object is not nested in the Competition object, the Competition should be able to map a relationship to the System object in the local database, because the id of the system is always provided.
The other problem I suspect is that I'm using two object persisters, the JacksonObjectPersister and the InDatabaseObjectPersisterFactory (provided by RoboSpice for use with ORMLite). The reason for this is because I don't want to persist the lists that the objects are nested in to the database. By my understanding, Jackson should cache the JSON response from the server, ORMLite should cache the data model.
This is the error I'm getting
02-03 15:15:57.640: D//DefaultRequestRunner.java:166(20944): 15:15:57.636 Thread-28
An exception occurred during service execution :org.codehaus.jackson.map.JsonMappingException:(was java.lang.NullPointerException)
(through reference chain: com.company.app.api.objects.List["items"]->
java.util.HashMap["51"]->
com.company.app.api.objects.Competition["system_id"])
Apologies for what seems like a number of questions in one, I am bashing my head against a wall. I will attempt to wrap this essay up with a summary...
Is there any way to piece together object relationships using Jackson and ORMLite for the JSON responses I have provided?

Categories

Resources