在房间数据库中使用关系和继承

问题描述 投票:0回答:1

我正在尝试创建一个简单的电子钱包应用程序,我想在任何交易后立即保存帐户余额的每次更改的详细信息以及当前余额。我想将这些信息保存在房间数据库中,但在处理房间继承时遇到问题。 基本上我有一个超类 BalanceChanges:

`open class BalanceChanges(

 open var type:Int,

 open var referenceid:String,

 open val date:String)`

以及几个子类,例如这个 TopUp 类:

`data class TopUp(

                 var amount:Int,

                 override val date: String, val bank:String,

                 val odersn:String,

                 override var referenceid:String,

                 override var type:Int = TYPE_TOPUP)

    :BalanceChanges(type,referenceid, date)`

我想保存的是这样的:

`class BalanceChangesWithCurrentBalance(

    val referenceid: String,

    val balanceChanges: BalanceChanges,

    val currentbalance: Int) {
}`

同时具有当前余额和一个 BalanceChanges(超类)的类代表多个子类,如 TopUp 类。那么我如何才能将该类插入到房间

我做了一些研究并考虑使用关系并使用共享且唯一的引用字段来购买所有类,但真正棘手的部分是我如何处理该超类,它是 子类。您是否应该对包含每个子类的表使用多重关系,或者我可以将所有子类存储在一个表中吗?

kotlin inheritance android-sqlite android-room relationship
1个回答
0
投票

使用条目存储天平很容易出现并发症,例如假设某个条目由于某种原因被删除或更新,那么该条目之后的所有条目都必须更新。

提取提取时计算出的运行余额并不难。

也许考虑以下演示。

首先是一些常量,可以简化查询的 SQL 等问题:-

const val DATABASE_VERSION: Int = 1
const val DATABASE_NAME = "the_database.db"
const val ENTRY_TABLE_NAME = "entry"
const val ENTRY_TIMESTAMP_COLUMN_NAME = "${ENTRY_TABLE_NAME}timestamp"
const val ENTRY_AMOUNT_COLUMN_NAME = "${ENTRY_TABLE_NAME}amount"
const val ENTRY_DESCRIPTION_COLUMN_NAME = "${ENTRY_TABLE_NAME}description"

该演示仅使用单个表来存储条目(不确定您想要什么/为什么需要子类):-

@Entity(tableName = ENTRY_TABLE_NAME)
class Entry(
    @PrimaryKey
    @ColumnInfo(name = ENTRY_TIMESTAMP_COLUMN_NAME)
    var entryTimestamp: Long=System.currentTimeMillis(),
    @ColumnInfo(name = ENTRY_AMOUNT_COLUMN_NAME)
    var entryAmount: Double,
    @ColumnInfo(name = ENTRY_DESCRIPTION_COLUMN_NAME)
    var entryDescription: String
)

要检索具有运行余额的条目,请使用以下 POJO:-

data class TransactionsWithBalance(
    @Embedded
    val entries: Entry,
    val balance: Double
)

最复杂的部分是

@Dao
带注释的界面,可用于插入和所有重要的摘录以及运行平衡:-

@Dao
interface AllDAO {
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    fun insertEntry(transaction: Entry): Long

    @Query(" SELECT " +
            "*" + /*<<<<< ALL columns from the table */
            /* an additional derived column named balance that:- */
            /* SELECTS the sum of the entryAmount From the entry table that have a lower or the same timestamp */
            /* after the constants are resolved the SQL generated for the sub query is :- */
            /*     (SELECT sum(entryamount) FROM `entry` WHERE entrytimestamp <= t.entrytimestamp) */
            /* Note that to differentiate between what is the same table used twice the AS t for the first use */
            ", (SELECT sum(${ENTRY_AMOUNT_COLUMN_NAME}) FROM `${ENTRY_TABLE_NAME}` WHERE ${ENTRY_TIMESTAMP_COLUMN_NAME} <= t.${ENTRY_TIMESTAMP_COLUMN_NAME}) AS balance " +
            "FROM ${ENTRY_TABLE_NAME} AS t ORDER BY ${ENTRY_TIMESTAMP_COLUMN_NAME}")
    fun getEntriesWithBalance(): List<TransactionsWithBalance>
}

为了让上面的内容被演示为一个

@Database
带注释的抽象类,它将以上所有内容联系在一起并允许创建/访问数据库:-

@Database(entities = [Entry::class], exportSchema = false, version = DATABASE_VERSION)
abstract class TheDatabase: RoomDatabase() {
    abstract fun getAllDAO(): AllDAO
    companion object {
        @Volatile
        private var instance: TheDatabase? = null
        fun getInstance(context: Context): TheDatabase {
            if (instance == null) {
                instance = Room.databaseBuilder(context,TheDatabase::class.java, DATABASE_NAME)
                    .allowMainThreadQueries() /* for brevity of demo */
                    .build()
            }
            return instance as TheDatabase
        }
    }
}

最后一些活动代码演示如何使用上面的代码插入一些数据,然后使用运行余额提取数据:-

class MainActivity : AppCompatActivity() {

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

        var insertCount = 0
        var attemptCount = 0
        for (i in 1..100 step 5) {
            attemptCount++
            if (dao.insertEntry(Entry(entryAmount = (i.toDouble() + (i.toDouble() / 10.00)), entryDescription = "i.")) > 0) insertCount++
        }
        Log.d("DBINFO","Inserted ${insertCount} rows, ${attemptCount} attempted")
        for (e in dao.getEntriesWithBalance()) {
            Log.d("DBINFO","Entry made on ${e.entries.entryTimestamp} for $${e.entries.entryAmount} desc being ${e.entries.entryDescription}. BALANCE ${e.balance}")
        }
    }
}

因此将插入 20 行(假设每次插入需要超过 1 毫秒),每行的数量根据迭代器的值增加,增加 5,所以是 1,然后是 6 ....,即值是迭代器的值加上迭代器的 1/10,这样就可以肯定使用 Double。

  • 并不是说双打真的应该用于财务方面,但那是另一个话题了。

当上面运行时(第一次),日志包括:-

2023-08-11 19:04:17.019 D/DBINFO: Inserted 20 rows, 20 attempted


2023-08-11 19:04:17.027 D/DBINFO: Entry made on 1691744656909 for $1.1 desc being i.. BALANCE 1.1
2023-08-11 19:04:17.028 D/DBINFO: Entry made on 1691744656953 for $6.6 desc being i.. BALANCE 7.699999999999999
2023-08-11 19:04:17.028 D/DBINFO: Entry made on 1691744656955 for $12.1 desc being i.. BALANCE 19.799999999999997
2023-08-11 19:04:17.028 D/DBINFO: Entry made on 1691744656972 for $17.6 desc being i.. BALANCE 37.4
2023-08-11 19:04:17.028 D/DBINFO: Entry made on 1691744656974 for $23.1 desc being i.. BALANCE 60.5
2023-08-11 19:04:17.028 D/DBINFO: Entry made on 1691744656976 for $28.6 desc being i.. BALANCE 89.1
2023-08-11 19:04:17.028 D/DBINFO: Entry made on 1691744656978 for $34.1 desc being i.. BALANCE 123.19999999999999
2023-08-11 19:04:17.028 D/DBINFO: Entry made on 1691744656980 for $39.6 desc being i.. BALANCE 162.79999999999998
2023-08-11 19:04:17.028 D/DBINFO: Entry made on 1691744656983 for $45.1 desc being i.. BALANCE 207.89999999999998
2023-08-11 19:04:17.028 D/DBINFO: Entry made on 1691744656986 for $50.6 desc being i.. BALANCE 258.5
2023-08-11 19:04:17.028 D/DBINFO: Entry made on 1691744656989 for $56.1 desc being i.. BALANCE 314.6
2023-08-11 19:04:17.028 D/DBINFO: Entry made on 1691744656992 for $61.6 desc being i.. BALANCE 376.20000000000005
2023-08-11 19:04:17.028 D/DBINFO: Entry made on 1691744656994 for $67.1 desc being i.. BALANCE 443.30000000000007
2023-08-11 19:04:17.028 D/DBINFO: Entry made on 1691744656996 for $72.6 desc being i.. BALANCE 515.9000000000001
2023-08-11 19:04:17.028 D/DBINFO: Entry made on 1691744656999 for $78.1 desc being i.. BALANCE 594.0000000000001
2023-08-11 19:04:17.029 D/DBINFO: Entry made on 1691744657000 for $83.6 desc being i.. BALANCE 677.6000000000001
2023-08-11 19:04:17.029 D/DBINFO: Entry made on 1691744657006 for $89.1 desc being i.. BALANCE 766.7000000000002
2023-08-11 19:04:17.029 D/DBINFO: Entry made on 1691744657009 for $94.6 desc being i.. BALANCE 861.3000000000002
2023-08-11 19:04:17.029 D/DBINFO: Entry made on 1691744657011 for $100.1 desc being i.. BALANCE 961.4000000000002
2023-08-11 19:04:17.029 D/DBINFO: Entry made on 1691744657014 for $105.6 desc being i.. BALANCE 1067.0000000000002

可以看出,余额是根据现有数据计算的,如果提取时删除或更新任何数据,余额就会发生变化。

  • 注意舍入问题,即存储为双精度/小数会导致不精确/近似值

通过应用程序检查,数据库本身的条目表为:-

© www.soinside.com 2019 - 2024. All rights reserved.