Room 实体的数据类

Data classe to Room entities

我有这个数据类

@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
        }
    }
}

现在我正在尝试使用房间实体实现相同的结果,以实现持久性和轻松性。 这是我的代码:

@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
)

谢谢

您似乎对如何创建适当的实体来表示您的原始 classes 并试图创建复杂的主键来创建 tables 之间的关系感到困惑。

我建议使用唯一 ID(单列)作为关系的基础。

以下代码基于对原始 classes 的分层检查并使用原始 class 名称作为数据库 Table/Entity 名称的基础是对您可能希望将其作为基础。

所以直接或间接引用的最顶层 class 是 IdTotalTotal class,看起来 idEnvioMarrocos 将是一个唯一值。

这是编码的内容(基本上与您编码的内容略有不同):-

@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 而不是 Int,因为我更喜欢使用 Long,至少对于 Java 值可以是 Long 而不是 int。
  • 不需要 autoGenerate = false 因为 @PrimaryKey 将默认为 false。
  • 请注意,我在原来的 class 名称前加上了 DB 以表明它是一个与 DB 相关的 class(我一直使用它)。

现在根据您原来的 classes,IdTotalToal class 有子 classes(也称为关系)与 IdTotal 的列表。所以 DBIdTotal 看起来像 :-

@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
)
  • 即DBIdTotal 是 DBIdTotalTotal 的子项,父项存储在已添加的 ref_DBIdTotalTotal 列中。
    • 您将看到一种模式,其中 ref_ 为添加的新列添加前缀以引用父列。
  • 注意(我没有在外键上包含索引 Room 会警告你这样做)

由于 IdTotal class 有两个列表,因此它是 IdPartPeso(s) 和 IdOpPedido(s) 的父级,这两个 DB classes 如下:-

@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
)

@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 有一份 PesoTamanho 的列表,所以 DBPesoTamanho 是 :-

@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
)

同样 IdOpPedido 有一份 Pedido 的 SO DBPedo 列表:-

@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
)

这涉及到 table。但是,您很可能想要获得包含所有底层对象的 DBIdTotalTotalDBIdTotal 的 .... DBPedido's 和 DBPesoTamanho's).

因此,我们这次创建 POJO 以将子项与父项合并到子项列表中来备份层次结构。

所以对于 DBIdOpPedido 我们想要 DBIdOpPedido 对象和 List 所以下面POJO 适合:-

data class PojoDBIdOpPedidoWithDbPedido (
    @Embedded
    val dbIdOpPedido: DBIdOpPedido,
    @Relation(
        entity = DBPedido::class,
        parentColumn = "id_DBIdOpPedido",
        entityColumn = "ref_DBIdOpPedido"
    )
    val dbPedidoList: List<DBPedido>
)
  • 父类是Embedded,子类使用@Relation

类似于 DBPartePpesoList :-

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

下一个层次结构是 DBIdTotal,它有 2 个列表和子列表。然而,它并不复杂,因为可以使用之前创建的 POJO。因此有:-

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>
)
  • 请注意,所指的实体是实体而非 POJO,因为它是 table 中构成关系的列。

所以最后但并非最不重要的是顶级 DBIdTotalTotal 也就是所有的东西(因此有点不同的名字):-

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

以上代码可以配合适当的Dao's nothing Room构建底层查询,在使用@Relation时获取ALL关系。这使得查询非常简单,因为您只需指定更高级别 table。以下是 suitable Dao 的(在一个名为 DBDao 的 class 中)根据 :-

@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>

}
  • 并非具有底层@Relation 的查询都具有@Transaction(如果没有,Room 将发出警告(最好使用@Transaction,因为子对象是通过使用其他查询构建的))。

Testing/Demo/Example

创建了名为 *TheDatabase 的@Database :-

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

然后将以下内容用于 testing/demonstrating 上面的代码(注意 allowMainThreadQueries 用于简洁和方便):-

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}")
    }
}
  • 以上是为 运行 设计的,并且只为 testing/demonstrating 工作一次。

结果

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
  • 也就是Single IdTotalTotal提取出来跟底层相关的对象
  • 以上是为了说明原理,输出结果没有严格检查是否符合预期,可能存在一些问题。