Search code examples
kotlinmany-to-manyandroid-room

Kotmin Room crossRef Entity with keys having the same name


I'm creating a CRUD factory. Basically all my entites will inherit from a BaseEntity with id as a primary key I'm trying to understand how to create a cross ref table for a M2M relationship. Here is a simplifyied example, without inheritance. ArticlesEntity have many MagasinsEntity and MagasinsEntity many ArticlesEntity. The Entity ArticlesMagasinsCrossRef is the junction

But both ArticlesEntity and MagasinsEntity have id as the primaryKey.

@Entity(
    tableName = "articles"
)
data class ArticlesEntity(
    @PrimaryKey(autoGenerate = false) val id: UUID = UUID.randomUUID(),
    val title: String,
)

@Entity(tableName = "magasins")
data class MagasinsEntity(
    @PrimaryKey(autoGenerate = false) val id: UUID = UUID.randomUUID(),
    val nomMagasin: String

)

@Entity(
    tableName = "articles_magasins"
   )

data class ArticlesMagasinsCrossRefEntity(
    val id: UUID, // how is it possible here to have the id of Articles ?
    val id: UUID // how is it possible here to have the id of Magasins ?
)

Edit I tried of course to change the name of the columns:

data class ArticlesMagasinsCrossRefEntity(
            val articleRd: UUID
            val magasinId: UUID 
        )

but the build failed for the relation data class :for example

data class RelMagasinWithArticles(

    @Embedded val magasin: MagasinsEntity,
    @Relation(
        parentColumn = "magasinId",
        entityColumn = "id",
        associateBy = Junction(ArticlesMagasinsCrossRefEntity::class)
    )
    val articles: List<ArticleEntity>

)

enter image description here


Solution

  • You need to use the Junction's parentColumn and entityColumn parameters e.g.

    data class RelMagasinWithArticles(
    
        @Embedded val magasin: MagasinsEntity,
        @Relation(
            parentColumn = "id", /* The column in the @Embedded table (articles) */
            entityColumn = "id", /* The column in the Related table (magasins) */
            associateBy = Junction(
                ArticlesMagasinsCrossRefEntity::class,
                parentColumn = "magasinId", /* The column in the junction table that maps to the @Embedded table */
                entityColumn = "articleRd" /* The column in the junction table that maps to the @Relation Table */
            )
        )
        val articles: List<ArticlesEntity>
    )
    

    Note

    You will also need to define a primary key for the ArticlesMagasinsCrossRefEntity class e.g. :-

    @Entity(
        tableName = "articles_magasins",
        /*<<<<< all Room table MUST have a primary key */
        /* as primary key on a single column would be restrictive use a composite
            primary key
         */
        primaryKeys = ["articleRd","magasinId"] 
    )
    
    data class ArticlesMagasinsCrossRefEntity(
    
        /* Cannot have identical member names - i.e. only 1 could be id
        val id: UUID, // how is it possible here to have the id of Articles ?
        val id: UUID // how is it possible here to have the id of Magasins ?
         */
        val articleRd: UUID,
        /* Room will issue warning if the is no index on the 2nd column */
        @ColumnInfo(index = true)
        val magasinId: UUID
    )
    
    • note that you can use the @ColumnInfo name parameter to specify column names.

    Demonstration

    So using you code with the suggested code overriding your code and with the following @Dao interface:-

    @Dao
    interface AllDao {
    
        @Insert
        fun insert(articlesEntity: ArticlesEntity)
        @Insert
        fun insert(magasinsEntity: MagasinsEntity)
        @Insert
        fun insert(articlesMagasinsCrossRefEntity: ArticlesMagasinsCrossRefEntity)
    
        @Query("SELECT * FROM magasins")
        @Transaction
        fun getMWA(): List<RelMagasinWithArticles>
    }
    

    a suitable @Database annotated class and code in the activity:-

    lateinit var db: TheDatabase
    lateinit var dao: AllDao
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        db = TheDatabase.getInstance(this)
        dao = db.getAllDao()
    
        val a1uuid = UUID.randomUUID()
        val a2uuid = UUID.randomUUID()
        val a3uuid = UUID.randomUUID()
        dao.insert(ArticlesEntity(a1uuid, "Article1"))
        dao.insert(ArticlesEntity(a2uuid,"Article2"))
        dao.insert(ArticlesEntity(a3uuid,"Article3"))
    
        val m1uuid = UUID.randomUUID()
        val m2uuid = UUID.randomUUID()
        val m3uuid = UUID.randomUUID()
        dao.insert(MagasinsEntity(m1uuid,"Magasin1"))
        dao.insert(MagasinsEntity(m2uuid,"Magasin2"))
        dao.insert(MagasinsEntity(m3uuid,"Magasin3"))
    
        dao.insert(ArticlesMagasinsCrossRefEntity(a1uuid,m2uuid))
        dao.insert(ArticlesMagasinsCrossRefEntity(a1uuid,m3uuid))
    
        dao.insert(ArticlesMagasinsCrossRefEntity(a2uuid,m1uuid))
    
        dao.insert(ArticlesMagasinsCrossRefEntity(a3uuid,m1uuid))
        dao.insert(ArticlesMagasinsCrossRefEntity(a3uuid,m2uuid))
        dao.insert(ArticlesMagasinsCrossRefEntity(a3uuid,m3uuid))
    
        val sb = StringBuilder()
        for(mwa in dao.getMWA()) {
            sb.append("\nMagasin is ${mwa.magasin.nomMagasin}. ID is ${mwa.magasin.id} it has ${mwa.articles.size} articles. They are:-" )
            for (article in mwa.articles) {
                sb.append("\n\tArticle is ${article.title} ID is ${article.id}")
            }
        }
        Log.d("DBINFO",sb.toString())
    }
    

    The output to the log is:-

    D/DBINFO: Magasin is Magasin1. ID is 0f3384ee-6232-423e-b2f1-a12ebdac6487 it has 2 articles. They are:-
            Article is Article2 ID is 2729d017-de05-41d2-8de3-8351dfca0a6b
            Article is Article3 ID is 42057ea7-bc03-409f-b2b8-2dc3fa5def19
        Magasin is Magasin2. ID is ba649833-a8ce-4cf2-a1b8-bcab8f7a7d0a it has 2 articles. They are:-
            Article is Article1 ID is 8763421d-b86d-4725-8e6b-65570958ebdc
            Article is Article3 ID is 42057ea7-bc03-409f-b2b8-2dc3fa5def19
        Magasin is Magasin3. ID is eed6f0a5-0825-4cda-9eb4-c4e973a49738 it has 2 articles. They are:-
            Article is Article1 ID is 8763421d-b86d-4725-8e6b-65570958ebdc
            Article is Article3 ID is 42057ea7-bc03-409f-b2b8-2dc3fa5def19