Android Room Entities: Is it possible to ignore fields by default? - android

By default, Room creates a column for each field that is defined in the entity. If an entity has fields that I don't want to persist, I have to use the #Ignore annotation.
This poses a problem with inheritance. Annotating all the unwanted fields from a base class becomes unfeasible at a certain point, especially if you have to make your own versions of complex stock objects only to ignore the fields.
Currently, I am using interfaces instead of inheritance to work around that problem, but I would prefer to have a base class for my Room objects.
Do you know a way to ignore fields by default, so I can rather De-Ignore the desired fields instead of the other way around? Preferably in Kotlin?
Edit:
I want to build a treeview of different room entities and it would be nice to have my treeview item as a base class for all of them. But the treeview item implements a lot of stuff, it is not practical to customize all of that just for #Ignore tags. There are workarounds, but i would need less code if i do it this way.

You can use #Ignore on your base classes as well, for instance:
open class MyBaseClass{
#Ignore
open var somethingBasic: Int = 0
}
#Entity(...)
class A : MyBaseClass{
var name: String? = null
}
#Entity(...)
class B : MyBaseClass{
var type: Int = 0
}
But be careful about this because using a base class for different tables is a bit abnormal and it's able to break all your tables somewhere (take migrations as an example). I suggest to take a deeper look on your structure and try to stay away from this :D

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.

Private setter vs Backing Property in Kotlin

I'm an old Java fan and trying to understand Kotlin basics. Can someone tell me what is the difference between these codes:
private val _users = mutableListOf<User>()
val users: List<User>
get() = _users
VS
var _users: mutableListOf<User>()
private set
As far as understand, upper code provides set _users only in that class and get it publicly which seems pretty same with the code below but in Google docs they say it's backing property which i don't get what it is.
Both examples are really totally different. You need to understand the difference between:
val MutableList
and:
var List
First makes possible to modify the contents of a list, but you can't replace the list object itself. Second, you can replace the list object, but you cannot modify its contents. This isn't really specific to Kotlin, it is the same in Java. val (or making a setter private) is like final field and List is like wrapping a list with Collections.unmodifiableList() - they're different things.
In your first example, neither the external nor internal code can replace the list, but internal code can modify its contents - external can't. In second example both internal and external code can modify contents of the list, but only internal code can replace the list entirely.

Truth ignore field

I am looking for Guava Truth equivalent of AssertJ usingElementComparatorIgnoringFields to ignore some field.
Exemple:
data class CalendarEntity(
#PrimaryKey(autoGenerate = true)
var id: Int = 0,
var name: String
)
Truth.assertThat(currentCalendars).containsExactlyElementsIn(expectedCalendars) // Here I want to ignore the id field
Thanks for your help.
For Truth, we decided not to provide reflection-based APIs, so there's no built-in equivalent.
Our general approach to custom comparisons is Fuzzy Truth. In your case, that would look something like this (Java, untested):
Correspondence<CalendarEntity, CalendarEntity> ignoreId =
Correspondence.from(
(a, b) -> a.name.equals(b.name),
"fields other than ID");
assertThat(currentCalendars).usingCorrespondence(ignoreId).containsExactlyElementsIn(expectedCalendars);
If you anticipate wanting this a lot (and you want to stick with Truth rather than AssertJ), then you could generalize the ignoreId code to work with arbitrary field names.
(Also: In this specific example, your CalendarEntity has only one field that you do want to compare. In that case, you can construct the Correspondence in a slightly simpler way: Correspondence.transforming(CalendarEntity::name, "name").)

Is it proper to use ViewModel for the ListView and the <T> in the ListView?

Preamble
In trying to get my head around the Kotlin classes to implement Android's ViewModel (and MVVM pattern) as used with Fragments and Activities, it is not clear to me of the trade-offs among the various complex classes especially how they have inherited implicit operations and visible methods (e.g., from the observer objects, managed scope, etc.) versus the old O-O approach of passing list-items and lists between activities in an intent as a bundle or reference, etc.
To illustrate my learning dilemma, I am implementing a crunchy cookie and and a jar to contain the cookies. The cookies can be created, consumed and viewed inside the cookie jar.
Android code tends to be vague on details of classes and the tutorials use deprecated versions, so it is difficult to follow best-practices with the latest version of the Android Architecture Component libraries.
Pseudo Kotlin code:
data class CrunchieCookie : {
var flavor: String?
var calories: String?
var photo: ImageView?
}
class CrunchieCookieViewModel : ViewModel() {
val _crunchieCookie: CrunchieCookie?
val crunchieCookie: CrunchieCookie = _crunchieCookie
}
class CookieJarListViewModel: ViewModel() {
val _cookieJar: MutableLiveData<CrunchieCookie>?
val cookieJar: LiveData<CrunchieCookie> = _cookieJar
}
Purpose
I am expecting to create, update and destroy crunchie-cookies
I am expecting to put crunchie-cookies in a cookie-jar (and take them out)
I am expecting to list all the crunchie-cookies in the cookie-jar in a scrolling ListView
I am expecting to click on a crunchie-cooking in the cookie-jar to open an detail view of the cookie
Finally, storing the cookie-jar in a remote DB, so planning for the local/remote data-source in the future
So, to my way of thinking, the cookie viewmodel will be used in CRUD operations and reused in the detail view from the list model.
MAKING #Tenfour04 's COMMENT AN ANSWER.
Your ViewModel should have a LiveData<List>. The Fragment containing the ListView should observe the LiveData for changes and pass the List along to the ListView when the LiveData value changes. If you're actually just modifying the contents of a MutableList, then you need to set the value of the MutableLiveData to that same list to inform it that there's a change it needs to notify observers about. – Tenfour04 Sep 9 at 0:02

How to integrate Kotlin inline class with Android data binding?

Kotlin introduced inline class which is strong typed type alias. This can be useful when use with database. For example,
inline class Age(val value: Int)
inline class Height(val value: Int)
When they are written to database, they are compiled to Int but Kotlin can prevent you accidentally putting a Height into a Age Field. If you use type alias or Int directly, it is possible with type alias but inline class produces a compile time error.
However, these also cause problems with Android data binding. I get data binding error when I try to bind a String inline class to a String attribute.
While it is possible to write some kinds of adapter to bypass this, but it defeat the purpose of using inline class and not practical for creating adapters for all inline classes.
I would like to ask are there any elegant ways to solve this issue?
First thing you need to understand is inline classes are not just wrappers around primitive types. They are more than type Aliases.
Now coming to your example, even though DataBinding has the understanding that if you put any MutableLiveData<T> instance in xml, it will take that value of that particular variable(something like mutableLiveData.value). But if you put MutablLiveData<Age>, mutableLiveData.value will always be of Type Age but not type Int.
Note that inline class, creates a completely new type and not just a type alias.
I believe that you somehow need a method in your data binding, that returns the value contained in the inline class object.

Categories

Resources