android-roomandroid-room-relation

Android Room "One-to-Many" relationships leading to entity-related errors in Gradle/KSP


I'm currently attempting to develop a small and simple database for a project of mine, and am running into this one error that's eluding me. This shows up during Gradle's kspDebugKotlin portion of the build, where I receive three errors, but it's the second that seems to be the problem I'm suffering from:

Not entirely sure what I'm messing up on to get this error, as after looking over Android Developer's tutorial on One-To-Many relationships in Room (this one here), I believe it's almost at the state that it works, but Gradle just outright refuses to let this through.

When I went into developing this, I saw the @Embedded tag that could be used, so I used this, but it seems to be the part that's possibly throwing this error, but I'm not so sure now.

What should be happening is the User being the parent of the Activities class (one User can have many Activities, for example), which is why I have the entity stated within the @Embedded portion of the relational class for them both, but that never solved the issue. Even adding in a foreign key to the Activities entity failed to resolve this.

Here's the code that I have to better show what seems to be the problem:

Room.kt - User:

// Users Table
@Entity(indices = [Index(value = ["email"], unique = true)], tableName = "users")
data class User(
    @PrimaryKey(autoGenerate = true) val userId: Int,
    @ColumnInfo(name = "username") val userName: String,
    @ColumnInfo(name = "first_name") val firstName: String,
    @ColumnInfo(name = "last_name") val lastName: String?,
    @ColumnInfo(name = "email") val email: String
)

Room.kt - Activities:

// Activities Table
@Entity(tableName = "activities")
data class Activities(
    @PrimaryKey(autoGenerate = true) val activityId: Int,
    val userCreatorId: Int,
    @ColumnInfo(name = "activity_date") val activityDate: Long,
    val details: String
)

Room.kt - UserAndActivities Relational Data Class:

// User and Activities Relationship
data class UserAndActivities(
    @Embedded
    val user: User,
    @Relation(
        entity = Activities::class,
        parentColumn = "userId",
        entityColumn = "userCreatorId"
    )
    val userActivities: List<Activities>
)

Room.kt - UserDao:

@Dao
interface UserDao {
    @Query("SELECT * FROM User")
    fun getAll(): List<User>

    @Insert
    fun insertUser(vararg users:User)

    @Update
    fun updateUser(vararg users:User)

    @Delete
    fun deleteUser(vararg users:User)

    @Transaction
    @Query("SELECT * FROM User")
    fun getUsersWithActivities(): List<UserAndActivities>
}

I do hope this is enough to help with figuring out my issue with this. I have attempted to use other answers on the sight to similar questions, yet this error still persisted. Given what other ways this was solved on the site, however, makes me question if the relational class is even needed for this one-to-many at all, and simply using a JOIN SQL query would just do the trick.
My apologies in advance if this has been answered ad-nauseam already, I've just run out of ideas on what to do for this, so any help at all is greatly appreciated here.


Solution

  • Entities cannot have relations.

    This is because the UserAndActivities class has been declared as an entity in in the entities parameter of the @Database annotation.

    And thus:-

    wrong

    As opposed to:-

    correct

    Given what other ways this was solved on the site, however, makes me question if the relational class is even needed for this one-to-many at all, and simply using a JOIN SQL query would just do the trick.

    No as the query would return a row for every combination (a cartesian product), so if a user has 3 activities then for that user there would be 3 rows and thus 3 objects each with the user and the respective activity.

    :-

    "SELECT `activityId`,`notTheUserCreatorId`,`activity_date`,`details`,`theReferenceToTheParentUser` FROM `activities` WHERE `theReferenceToTheParentUser` IN (
    

    However, as you have specified the primary key column, WHICH IS IMPLICTLY UNIQUE, as the column that relates the Activity (child) to the User (Parent) then you have a 1-1 relationship.

    You very likely want something along the lines of:-

    // Activities Table
    @Entity(tableName = "activities")
    data class Activities(
        @PrimaryKey(autoGenerate = true) val activityId: Int,
        val notTheUserCreatorId: Int,
        @ColumnInfo(name = "activity_date") val activityDate: Long,
        val details: String,
        @ColumnInfo(index = true) /* indexing should improve efficiency */
    val theReferenceToTheParentUser: Int
    )
    //@Entity
    // User and Activities Relationship
    data class UserAndActivities(
        @Embedded
        val user: User,
        @Relation(
            entity = Activities::class,
            parentColumn = "userId",
            entityColumn = "theReferenceToTheParentUser"
        )
        val userActivities: List<Activities>
    )
    

    So for example you may have data something like:-

    i.e.