android Room with kotlin value class?
Asked Answered
M

4

13

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

Mace answered 3/10, 2021 at 15:48 Comment(1)
It looks like it is not yet supported.Respiratory
C
9

According to the Google Issue Tracker, value classes are now supported on Room version 2.6.0-alpha01. To be able to build, please follow these steps:

Use KSP instead of KAPT for the Room compiler. Add the plugin to the root build.gradle, or you can refer to the Migrate from KAPT to KSP guide

Add to the project build.gradle

plugins {
    ...
    id 'com.google.devtools.ksp' version '1.8.10-1.0.9' apply false
}

In the app build.gradle

plugins {
    ....
    id 'com.google.devtools.ksp'
}

android {
    ...
    ksp {
        arg("room.generateKotlin", "true")
    }
}

dependencies {
    ...
    implementation "androidx.room:room-ktx:2.6.0-alpha01"
    ksp "androidx.room:room-compiler:2.6.0-alpha01"
}

Optional: If the value class has a public constructor, you do not need to write a TypeConverter for it anymore. However, if it has a private constructor or if it is a third-party class with an internal constructor, you need to provide a TypeConverter to build it. For example, kotlin.time.Duration.

class Converters {

    @TypeConverter
    fun formDuration(value: Duration?): Long? {
        return value?.inWholeMilliseconds
    }

    @TypeConverter
    fun toDuration(time: Long?): Duration? {
        return time?.let { time.milliseconds }
    }
}

@Database(
    ...
)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {
    ...
}
Chiropractic answered 7/4, 2023 at 15:27 Comment(1)
I was able to get my Duration class working with these instructions. Just updating the version didn't fix it, I had to to have the generateKotlin ksp args as well. Thank youPyrogenic
H
2

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). 👎

Hebdomadal answered 24/5, 2022 at 14:23 Comment(0)
S
-2

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.

Scarify answered 3/10, 2021 at 16:0 Comment(1)
TypeConverters don't work for value classesClarify
B
-2

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.

Bubaline answered 3/10, 2021 at 16:36 Comment(2)
"just use token: String directly on your entity class" -- the reason may be to have a type-safe ID.Respiratory
TypeConverters don't work for value classesClarify

© 2022 - 2024 — McMap. All rights reserved.