1

I' ve this data classe

@Parcelize
data class IdTotalTotal(
    val idEnvioMarrocos: Int,
    val clifor: String,
    val nomeClifor: String,
    val nomeUser: String,
    val termina: Int,
    var dados: List<IdTotal>
): Parcelable {
    @Parcelize
    data class IdTotal(
        val produto: String,
        val modelo: String,
        var idPartePeso: List<IdPartePeso>,
        var idOpPedido: List<IdOpPedido>
    ) : Parcelable {
        @Parcelize
        data class IdPartePeso(
            val parteID: Int,
            val nomeParte: String,
            var pesoTamanho: List<PesoTamanho>
        ) : Parcelable {
            @Parcelize
            data class PesoTamanho(
                val nomeT: String,
                var pesoT: Int
            ) : Parcelable
        }
        @Parcelize
        data class IdOpPedido(
            val op: String,
            var pedidos: List<Pedido>
        ) : Parcelable {
            @Parcelize
            data class Pedido(
                val pedido: String,
                val pedidoCliente: String,
                var pedidoEntra: Boolean
            ) : Parcelable
        }
    }
}

Now I'm trying to achieve the same result with a room entity for persistence and ease. This is my code:

@Entity
data class EnviosPesosDestinosBd(
    @PrimaryKey(autoGenerate = false)
    val idEnvioMarrocos: Int,
    val clifor: String,
    val nomeClifor: String,
    val nomeUser: String,
    val termina: Int,
    val updatedAt: Long = System.currentTimeMillis()
)

@Entity(primaryKeys = ["idEnvioMarrocos", "produto"])
data class EnvioProduto(
    val idEnvioMarrocos: Int,
    val produto: String,
    val modelo: String,
)


@Entity(primaryKeys = ["idEnvioMarrocos", "produto", "parteID"],
    foreignKeys = [
        ForeignKey(
            entity = TamanhoPeso::class,
            parentColumns = ["idEnvioMarrocos", "produto", "parteID"],
            childColumns = ["idEnvioMarrocos", "produto", "parteID"]
        )
    ]
)
data class PesoParte(
    val idEnvioMarrocos: Int,
    val produto: String,
    val parteID: Int,
    val nomeParte: String,
)


@Entity(primaryKeys = ["idEnvioMarrocos", "produto", "parteID", "nomeT"])
data class TamanhoPeso(
    val idEnvioMarrocos: Int,
    val produto: String,
    val parteID: Int,
    val nomeT: String,
    var pesoT: Int
)

@Entity(primaryKeys = ["idEnvioMarrocos", "op", "produto"],
    foreignKeys = [
        ForeignKey(
            entity = OpPedido::class,
            parentColumns = ["idEnvioMarrocos", "op"],
            childColumns = ["idEnvioMarrocos", "op"]
        )
    ])
data class EnvioDestinoComOp(
    val idEnvioMarrocos: Int,
    val produto: String,
    val op: String,
)

@Entity(primaryKeys = ["idEnvioMarrocos", "op", "pedido"])
data class OpPedido(
    val idEnvioMarrocos: Int,
    val op: String,
    val pedido: String,
    val pedidoCliente: String,
    var pedidoEntra: Boolean
)

Thanks

lpizzinidev
  • 12,741
  • 2
  • 10
  • 29

1 Answers1

1

It would appear that you are stuck with how to create appropriate Entities to represent your original classes and trying to make complex primary keys to create the relationships between the tables.

I would suggest utilising unique id's (single columns) as the basis for the relationships.

The following code, based upon a hierarchical inspection of your original classes and using the original class names as the basis for the Database Table/Entity names is a quick interpretation of what you may wish to consider as the basis.

So the topmost class directly or indirectly referenced is the IdTotalTotal class, and it appears that idEnvioMarrocos would be a unique value.

This what was coded (basically little different to what you coded) :-

@Entity
data class DBIdTotalTotal(
    @PrimaryKey
    val idEnvioMarrocos: Long,
    val clifor: String,
    val nomeClifor: String,
    val nomeUser: String,
    val termina: Int,
    val updatedAt: Long = System.currentTimeMillis()
)
  • Long rather Int has been used as I prefer to use Long as at least for Java the value can be Long rather than int.
  • No need for autoGenerate = false as just @PrimaryKey will default to false.
  • Note that I have prefixed the original class name with DB to indicate that it's a DB related class (and I've used this throughout).

Now according to your original classes the IdTotalToal class has sub classes (aka a relationship) with a list of IdTotal's. So DBIdTotal looks like :-

@Entity(
    foreignKeys = [
        ForeignKey(
            entity = DBIdTotalTotal::class,
            parentColumns = ["idEnvioMarrocos"],
            childColumns = ["ref_DBIdTotalTotal"]
        )
    ]
)
data class DBIdTotal(
    @PrimaryKey
    val id_DBIdTotal: Long,
    val ref_DBIdTotalTotal: Long,
    val produto: String,
    val modelo: String
)
  • i.e. A DBIdTotal is a child of a DBIdTotalTotal and the parent is stored in the ref_DBIdTotalTotal column that has been added.
    • you will see a pattern where ref_ prefixes the new column added to reference the parent column.
  • note (I have not included indexes on the Foreign keys Room will warn you to do this)

As the IdTotal class has two lists then it is a parent to the IdPartPeso(s) and the IdOpPedido(s) these two DB classes are as follows:-

@Entity(
    foreignKeys = [
        ForeignKey(
            entity = DBIdTotal::class,
            parentColumns = ["id_DBIdTotal"],
            childColumns = ["ref_DBIdTotal"]
        )
    ]
)
data class DBIdPartePeso(
    @PrimaryKey
    val id_DBIdPartePeso: Long,
    val ref_DBIdTotal: Long,
    val parteID: Int,
    val nomeParte: String
)

and

@Entity(
    foreignKeys = [
        ForeignKey(entity = DBIdTotal::class,
            parentColumns = ["id_DBIdTotal"],
            childColumns = ["ref_DBIdTotal"]
        )
    ]
)
data class DBIdOpPedido(
    @PrimaryKey
    val id_DBIdOpPedido: Long,
    val ref_DBIdTotal: Long,
    val op: String
)

IdPartePeso has a list of PesoTamanho's so DBPesoTamanho is :-

@Entity(
    foreignKeys = [
        ForeignKey(
            entity = DBIdPartePeso::class,
            parentColumns = ["id_DBIdPartePeso"],
            childColumns = ["ref_DBIdPartePeso"]
        )
    ]
)
data class DBPesoTamanho(
    @PrimaryKey
    val id_DBPesoTamanho: Long,
    val ref_DBIdPartePeso: Long,
    val nomeT: String,
    var pesoT: Int
)

Likewise IdOpPedido has a list of Pedido's SO DBPedido is :-

@Entity(
    foreignKeys = [
        ForeignKey(
            entity = DBIdOpPedido::class,
            parentColumns = ["id_DBIdOpPedido"],
            childColumns = ["ref_DBIdOpPedido"]
        )
    ]
)
data class DBPedido(
    @PrimaryKey
    val id_DBPedido: Long,
    val ref_DBIdOpPedido: Long,
    val pedido: String,
    val pedidoCliente: String,
    var pedidoEntra: Boolean
)

That deals with the tables. However, it is very likely that you want to get a DBIdTotalTotal with all the underlying objects (The DBIdTotal's .... the DBPedido's and DBPesoTamanho's).

So we work back up the hierachy this time creating POJO's for combining children with the parent into a List of the children.

So for a DBIdOpPedido we want the DBIdOpPedido object with a List so the following POJO would suit:-

data class PojoDBIdOpPedidoWithDbPedido (
    @Embedded
    val dbIdOpPedido: DBIdOpPedido,
    @Relation(
        entity = DBPedido::class,
        parentColumn = "id_DBIdOpPedido",
        entityColumn = "ref_DBIdOpPedido"
    )
    val dbPedidoList: List<DBPedido>
)
  • The parent is Embedded, the children use and @Relation

Similar for DBPartePpeso with the List :-

data class PojoDBIdPartePesoWithDBPesoTamanho(
    @Embedded
    val dbIdPartePeso: DBIdPartePeso,
    @Relation(
        entity = DBPesoTamanho::class,
        parentColumn = "id_DBIdPartePeso",
        entityColumn = "ref_DBIdPartePeso"
    )
    val dbPesoTamanhoList: List<DBPesoTamanho>
)

Next up the Hierarchy is the DBIdTotal, this has 2 lists which sublists. However, it's not complicated because the previously created POJO's can be utilised. As such there is:-

data class PojoDBIdTotalWithDBIdPartePesoAndWIthDBIdOpPedido(
    @Embedded
    val dbIdTotal: DBIdTotal,
    @Relation(
        entity = DBIdPartePeso::class,
        parentColumn = "id_DBIdTotal",
        entityColumn = "ref_DBIdTotal"
    )
    val pojoDBIdPartePesoWithDBPesoTamanhoList: List<PojoDBIdPartePesoWithDBPesoTamanho>,
    @Relation(
        entity = DBIdOpPedido::class,
        parentColumn = "id_DBIdTotal",
        entityColumn = "ref_DBIdTotal"
    )
    val pojoDBIdOpPedidoList: List<PojoDBIdOpPedidoWithDbPedido>
)
  • NOTE the entities referred to are the entities NOT the POJO's as it is the columns of the tables that form the relationships.

So last but not least the top level DBIdTotalTotal aka everything all together (as such a bit of a different name for this) :-

data class PojoDBIdTotalTotalWithDBIdTotalEtcetera(
    @Embedded
    val dbIdTotalTotal: DBIdTotalTotal,
    @Relation(
        entity = DBIdTotal::class,
        parentColumn = "idEnvioMarrocos",
        entityColumn = "ref_DBIdTotalTotal"
    )
    val pojoDBIdTotalWithDBIdPartePesoAndWIthDBIdOpPedidoList: List<PojoDBIdTotalWithDBIdPartePesoAndWIthDBIdOpPedido>
)

The above code can be used with appropriate Dao's nothing that Room builds the underlying queries to obtain ALL relations when using @Relation. This makes the queries very simple as you only have to specfify the higher level table. The following are suitable Dao's (in a single class named DBDao) as per :-

@Dao
interface DBDao {

    @Insert
    fun insertDBIdTotalTotal(dbIdTotalTotal: DBIdTotalTotal): Long
    @Insert
    fun insertDBIdTotal(dbIdTotal: DBIdTotal): Long
    @Insert
    fun insertDBIdPartePeso(dbIdPartePeso: DBIdPartePeso): Long
    @Insert
    fun insertDBPesoTamanho(dbPesoTamanho: DBPesoTamanho): Long
    @Insert
    fun insertDBIdOpPedido(dbIdOpPedido: DBIdOpPedido): Long
    @Insert
    fun insertDBPedido(dbPedido: DBPedido): Long

    @Transaction
    @Query("SELECT * FROM dbidoppedido")
    fun getIdOpPedidoWithAllPedidos(): List<PojoDBIdOpPedidoWithDbPedido>
    @Transaction
    @Query("SELECT * FROM dbidpartepeso")
    fun getIdPartePesoWithAllPesoTamanhod(): List<PojoDBIdPartePesoWithDBPesoTamanho>
    @Transaction
    @Query("SELECT * FROM dbidtotal")
    fun getIdTotalWithAllIdPartePesosAndAllIdOpPedidos(): List<PojoDBIdTotalWithDBIdPartePesoAndWIthDBIdOpPedido>
    @Transaction
    @Query("SELECT * FROM dbidtotaltotal")
    fun getIdTotalTotalWithEverything(): List<PojoDBIdTotalTotalWithDBIdTotalEtcetera>

}
  • not that the queries that have an underlying @Relation all have @Transaction (Room will issue warnings if not (best to use @Transaction as child objects are built by using additional queries)).

Testing/Demo/Example

An @Database named *TheDatabase was created :-

@Database(entities = [
    DBIdTotalTotal::class,
    DBIdTotal::class,
    DBIdOpPedido::class,
    DBIdPartePeso::class,
    DBPesoTamanho::class,
    DBPedido::class
                     ],
    version = 1
)
abstract class TheDatabase: RoomDatabase() {
    abstract fun getDao(): DBDao
}

The the following was then used for testing/demonstrating the code above (note allowMainThreadQueries used for brevity and convenience) :-

class MainActivity : AppCompatActivity() {
    lateinit var db: TheDatabase
    lateinit var dao: DBDao
    val TAG = "MYDBINFO"
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        db = Room.databaseBuilder(this,TheDatabase::class.java,"mydb.db")
            .allowMainThreadQueries().build()
        dao = db.getDao()
        var firstTotalTotalId =  dao.insertDBIdTotalTotal(DBIdTotalTotal(1L,"CLIFOR1","CLIFORNAME1","USERNAME1",10))
        var firstTotal = dao.insertDBIdTotal(DBIdTotal(2L,firstTotalTotalId,"PRODUTO1","MODELO1"))
        var secondTotal = dao.insertDBIdTotal(DBIdTotal(3L,firstTotalTotalId,"PRODUTO2","MODELO2"))
        var firstPartePeso = dao.insertDBIdPartePeso(DBIdPartePeso(4L,firstTotal,100,"Parte100"))
        var secondPartePeso = dao.insertDBIdPartePeso(DBIdPartePeso(5L,secondTotal,200,"PARTE200"))
        var thirdPartePeso = dao.insertDBIdPartePeso( DBIdPartePeso(6,firstTotal,300,"PARTE300"))
        var fourthPartePes = dao.insertDBIdPartePeso(DBIdPartePeso(7,secondTotal,400,"PARTE400"))
        var firstPesoTamanho = dao.insertDBPesoTamanho(DBPesoTamanho(8,firstPartePeso,"PESONTAMANHO1000",1000))
        var secondPesoTamanho = dao.insertDBPesoTamanho(DBPesoTamanho(9,thirdPartePeso,"PARTEPESO2000",2000))
        var firstIdOpPedido = dao.insertDBIdOpPedido(DBIdOpPedido(10,firstTotal,"OPPEDIDO10000"))
        var sedcondIdOpPedido = dao.insertDBIdOpPedido(DBIdOpPedido(11,firstTotal,"OPPEDIDO2000"))
        var firstPedido = dao.insertDBPedido(DBPedido(12,firstIdOpPedido,"PEDIDO100000","CLIENTE1",true))

        var getall = dao.getIdTotalTotalWithEverything()
        for(all: PojoDBIdTotalTotalWithDBIdTotalEtcetera in getall) {
            logIdTotalTotal(all.dbIdTotalTotal,"")
            for(allTotals: PojoDBIdTotalWithDBIdPartePesoAndWIthDBIdOpPedido in all.pojoDBIdTotalWithDBIdPartePesoAndWIthDBIdOpPedidoList) {
                logIdTotal(allTotals.dbIdTotal,"\t")
                for(allIdPartePeso: PojoDBIdPartePesoWithDBPesoTamanho in allTotals.pojoDBIdPartePesoWithDBPesoTamanhoList) {
                    logIdPartePeso(allIdPartePeso.dbIdPartePeso,"\t\t")
                    for(pesoTamanho: DBPesoTamanho in allIdPartePeso.dbPesoTamanhoList) {
                        logPesoTamanho(pesoTamanho,"\t\t\t")
                    }
                }
                for(allIdOpPedido: PojoDBIdOpPedidoWithDbPedido in allTotals.pojoDBIdOpPedidoList) {
                    logIdOpPedido(allIdOpPedido.dbIdOpPedido,"\t\t")
                    for(pedido: DBPedido in allIdOpPedido.dbPedidoList) {
                        logPedido(pedido,"\t\t\t")
                    }
                }
            }
        }
    }

    private fun logPedido(p: DBPedido,prefix: String) {
        Log.d(TAG,"$prefix PEDIDIO = ${p.pedido} CLIENTE = ${p.pedidoCliente} ID=${p.id_DBPedido} ENTRA=${p.pedidoEntra} REFERENCES IDOPPedido=${p.ref_DBIdOpPedido}")
    }
    private fun logIdOpPedido(iop: DBIdOpPedido, prefix: String) {
        Log.d(TAG,"$prefix OP=${iop.op} ID=${iop.id_DBIdOpPedido} REFERNCES TOTAL=${iop.ref_DBIdTotal}"  )
    }
    private fun logPesoTamanho(pt: DBPesoTamanho, prefix: String) {
        Log.d(TAG,"$prefix NOME=${pt.nomeT} PESO=${pt.pesoT} ID=${pt.id_DBPesoTamanho} REFRENCES IDPARTEPESO=${pt.ref_DBIdPartePeso}")
    }
    private fun logIdPartePeso(ipp: DBIdPartePeso, prefix: String) {
        Log.d(TAG,"$prefix NOMEPARTE=${ipp.nomeParte} PARTEID=${ipp.parteID} ID=${ipp.id_DBIdPartePeso} REFERNCES TOTAL=${ipp.ref_DBIdTotal}")
    }
    private fun logIdTotal(it: DBIdTotal, prefix: String) {
        Log.d(TAG,"$prefix MODELO=${it.modelo} PRODUTO=${it.produto} ID=${it.id_DBIdTotal} REFERENCES TOTALTOTAL= ${it.ref_DBIdTotalTotal}")
    }
    private fun logIdTotalTotal(itt: DBIdTotalTotal, prefix: String) {
        Log.d(TAG,"$prefix NOMUSER=${itt.nomeUser} CLIFOR=${itt.clifor} NOMCLIFOR=${itt.nomeClifor}  TERMINA=${itt.termina} UPDATED= ${itt.updatedAt} ID=${itt.idEnvioMarrocos}")
    }
}
  • The above has been designed to run and work just the once purely for testing/demonstrating.

Result

D/MYDBINFO:  NOMUSER=USERNAME1 CLIFOR=CLIFOR1 NOMCLIFOR=CLIFORNAME1  TERMINA=10 UPDATED= 1620189450079 ID=1
D/MYDBINFO:      MODELO=MODELO1 PRODUTO=PRODUTO1 ID=2 REFERENCES TOTALTOTAL= 1
D/MYDBINFO:          NOMEPARTE=Parte100 PARTEID=100 ID=4 REFERNCES TOTAL=2
D/MYDBINFO:              NOME=PESONTAMANHO1000 PESO=1000 ID=8 REFRENCES IDPARTEPESO=4
D/MYDBINFO:          NOMEPARTE=PARTE300 PARTEID=300 ID=6 REFERNCES TOTAL=2
D/MYDBINFO:              NOME=PARTEPESO2000 PESO=2000 ID=9 REFRENCES IDPARTEPESO=6
D/MYDBINFO:          OP=OPPEDIDO10000 ID=10 REFERNCES TOTAL=2
D/MYDBINFO:              PEDIDIO = PEDIDO100000 CLIENTE = CLIENTE1 ID=12 ENTRA=true REFERENCES IDOPPedido=10
D/MYDBINFO:          OP=OPPEDIDO2000 ID=11 REFERNCES TOTAL=2
D/MYDBINFO:      MODELO=MODELO2 PRODUTO=PRODUTO2 ID=3 REFERENCES TOTALTOTAL= 1
D/MYDBINFO:          NOMEPARTE=PARTE200 PARTEID=200 ID=5 REFERNCES TOTAL=3
D/MYDBINFO:          NOMEPARTE=PARTE400 PARTEID=400 ID=7 REFERNCES TOTAL=3
  • That is the Single IdTotalTotal is extracted with the underlying related objects
  • The above is intended to show the principle, there may be some issues as the output hasn't been rigorously checked that it conforms to expectations.
MikeT
  • 51,415
  • 16
  • 49
  • 68
  • MikeT fantastic answer, thank you for your time and knowledge. Imagine this situation we have a IdTotalTotal that has a product, but and this product has 3 sizes and another IdTotalTotal that has the same product but only 2 sizes, would you answer solve the problem? One more time Thanks – Bernardino Alves May 05 '21 at 11:09
  • @BernardinoAlves I don't believe so, I believe that you may want a many-many relationship. **However, I only speak English, so am/was unsure what the tables represent**. For m-m you typically have a mapping table that primarily has 2 columns each referencing one of the tables in the m-m. (@Relation associateBy and @Junction). You may wish to look at https://en.wikipedia.org/wiki/Associative_entity and also https://developer.android.com/reference/kotlin/androidx/room/Junction. – MikeT May 05 '21 at 17:04
  • MikeT, perfectly understood and now implemented.Thanks – Bernardino Alves May 06 '21 at 08:54
  • @BernardinoAlves great stuff. – MikeT May 06 '21 at 09:40