Sergio B.
Sergio B.

Reputation: 1000

many-to-many relation in Room database. Kotlin Junction class ignored

I have the following Kotlin entities for Room ver. 2.2.5.

PARENT ENTITY

@Entity(tableName = "ITEM",
    indices = [Index(value = ["id"], unique = true), Index(value = ["code"], unique = true), Index(value = ["status"], unique = false)]
)
data class Item (
    @PrimaryKey @ColumnInfo(name = "id") override val id: UUID = UUID.randomUUID(),
    @ColumnInfo(name = "code") @NotNull val code: String,
    @ColumnInfo(name = "valid") val valid: Boolean = true,
    @ColumnInfo(name = "value") val value: Double?,
    @ColumnInfo(name = "price") val price: Double?,
    @ColumnInfo(name = "default_description") val defaultDescription: String?,
    @ColumnInfo(name = "description") val description: String?
)

CHILD ENTITY

@Entity(tableName = "LOCATION",
    indices = [Index(value = ["id"], unique = true), Index(value = ["code"], unique = true)]
)
data class Location (
    @ColumnInfo(name = "id") @PrimaryKey override val id: UUID = UUID.randomUUID(),
    @ColumnInfo(name = "code") val code: String,
    @ColumnInfo(name = "latitude") val latitude: Double?,
    @ColumnInfo(name = "longitude") val longitude: Double?,
    @ColumnInfo(name = "default_description") val defaultDescription: String?
)

JUNCTION ENTITY

@Entity(
    tableName = "ITEM_LOCATION_L",
    primaryKeys = [
        "item_id", "location_id"
    ],
    foreignKeys = [
        ForeignKey(entity = Item::class, parentColumns = ["id"], childColumns = ["item_id"]),
        ForeignKey(entity = Location::class, parentColumns = ["id"], childColumns = ["location_id"])
    ],
    indices = [
        Index("id"),
        Index("item_id"),
        Index("location_id")])
data class ItemLocationLink (
    @ColumnInfo(name = "id") override val id: UUID = UUID.randomUUID(),
    @ColumnInfo(name = "item_id") val itemId: UUID, /** Item ID - parent entity */
    @ColumnInfo(name = "location_id") val locationId: UUID, /** Location ID - child entity */
    @ColumnInfo(name = "quantity") val quantity: Double /** Quantity of the item in the referenced location */
)

RESULT CLASS

class ItemLocationRelation {
    @Embedded
    lateinit var item: Item
    @Relation(
        parentColumn = "id",
        entityColumn = "id",
        associateBy = Junction(value = ItemLocationLink::class, parentColumn = "item_id", entityColumn = "location_id")
    ) lateinit var locations: List<Location>
}

DAO INTERFACE

@Dao
interface ItemLocationLinkDao {

   @Transaction
    @Query("SELECT * FROM ITEM WHERE id = :itemId")
    fun getLocationsForItem(itemId: UUID): List<ItemLocationRelation>
}

DATABASE TYPE CONVERTER

class DBTypesConverter {

    @TypeConverter
    fun fromUUID(uid: UUID?): String? {

        if (uid != null)
            return uid.toString()
        return null
    }

    @TypeConverter
    fun toUUID(str: String?): UUID? {

        if (str != null) {
            return UUID.fromString(str)
        }
        return null
    }

    @TypeConverter
    fun fromDate(d: Date?): Long? {

        return d?.time
    }

    @TypeConverter
    fun toDate(l: Long?): Date? {

        return if (l != null) Date(l) else null
    }
}

When I call getLocationsForItem I get in return an instance of ItemLocationRelation with a valid Item but no child objects. I've checked the generated code and there is no sign of the Junction class. The generated code behaves like it is not a many-to-many relation, the Junction class is completely ignored, I can even specify a fake class in the @Junction attribute of the relation and the result would be exactly the same without errors.

If I add a function to the DAO class that returns the results of the following query:

select * from item 
inner join item_location_l as link on link.item_id = item.id
inner join LOCATION as l on link.location_id = l.id
where item.id = '99a3a64f-b0e6-e911-806a-68ecc5bcbe06'

I get 2 rows as expected. So the SQLite database is ok. Please help.

Upvotes: 1

Views: 436

Answers (1)

Sergio B.
Sergio B.

Reputation: 1000

So, in the end the problem was really subtle. For reasons I cannot explain the project I was working on had the following gradle settings (app gradle):

kapt "android.arch.persistence.room:compiler:2.2.5"

I replaced it with

kapt "androidx.room:room-compiler:2.2.5"

And then everything was fine. The hard-coded query generated by the plugin now contains the JOIN and it works....

Upvotes: 1

Related Questions