You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Room数据库一对一关联:Coin与CoinRevenue数据同步实现咨询

问题描述

我有两个Room实体:Coin和CoinRevenue,Coin用来存储其他货币对应的USD价格,实体代码如下:

@Entity(tableName = "coin")
data class Coin(
    @field:PrimaryKey(autoGenerate = false)
    var id: String = "",
    var symbol: String = "",
    var priceInUsd: Float = 0f
)

CoinRevenue用来记录用户持有的对应货币数量,和Coin是严格的一对一关联,我最初写的实体代码是这样的:

@Entity(tableName = "coinRevenue")
data class CoinRevenueNew(
    @field:PrimaryKey(autoGenerate = true)
    var id: Int = 0,
    var coin: Coin? = null,
    var amount: Float = 0f
)

现在我的需求是:从数据库获取CoinRevenue时,能同步拿到数据库中最新的Coin对象。比如先保存一个价格为1.0253的EUR对应的Coin,再保存关联这个Coin的CoinRevenue;之后更新EUR的Coin价格为2.522,希望CoinRevenue里的Coin对象也能同步更新。

我知道@Embedded会把内部对象的字段合并到父表,但这样更新Coin的时候没法同步到CoinRevenue;而用@Relation的时候官方要求用List或Set,但这里是严格的一对一关联。

现有DAO代码如下:

// Coin相关DAO
@Query("select * from coin order by rank")
fun getAllCoins(): Flowable<List<Coin>>
@Query("select * from coin where rank = 1")
fun getFirstCoin(): Maybe<Coin>
@Query("select * from coin where favourite = 1 order by rank")
fun getAllFavouriteCoins(): Flowable<List<Coin>>
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertCoin(coin: Coin)
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertCoins(coinsList: List<Coin>)

// CoinRevenue相关DAO
@Query("select * from coinRevenue order by rank")
fun getAllCoinsRevenue(): Flowable<List<CoinRevenue>>
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertCoinRevenue(coinRevenue: CoinRevenue)
@Delete()
fun deleteCoinRevenue(coinRevenue: CoinRevenue)

请问实现这个需求的最佳方式是什么?


解决方案

嘿,我来帮你搞定这个问题!其实Room对一对一关联的支持很灵活,只是需要调整下实体定义和DAO的写法,就能完美实现你要的“获取CoinRevenue时同步拿到最新Coin数据”的需求,具体步骤如下:

第一步:修正实体定义,用外键替代直接嵌套对象

首先要改掉你原来在CoinRevenue里直接放Coin对象的写法——Room不会自动维护这种嵌套对象的同步,我们需要用外键来建立两个表的关联:

1.1 重构CoinRevenue实体

添加外键字段coinId,指向Coin表的主键id,同时定义外键约束(可选但推荐,保证数据完整性):

@Entity(
    tableName = "coinRevenue",
    foreignKeys = [ForeignKey(
        entity = Coin::class,
        parentColumns = ["id"],
        childColumns = ["coinId"],
        onDelete = ForeignKey.CASCADE // 可选,根据业务需求设置删除策略,比如删除Coin时自动删除关联的CoinRevenue
    )]
)
data class CoinRevenue(
    @PrimaryKey(autoGenerate = true)
    var id: Int = 0,
    var coinId: String = "", // 外键,关联Coin表的id字段
    var amount: Float = 0f
)

1.2 创建关联查询的POJO

定义一个专门用来承载关联结果的数据类,用@Relation标记一对一的关联关系——虽然Room文档里@Relation常用集合,但一对一场景下直接返回单个Coin对象完全可行:

data class CoinRevenueWithCoin(
    @Embedded val coinRevenue: CoinRevenue, // 嵌入CoinRevenue的所有字段
    @Relation(
        parentColumn = "coinId", // CoinRevenue的外键字段
        entityColumn = "id" // Coin表的主键字段
    ) val coin: Coin // 一对一关联的Coin对象
)

第二步:修改DAO,添加带事务的关联查询方法

在DAO里添加@Transaction注解的查询方法,Room会自动执行关联查询,并且保证查询的原子性,同时返回包含最新Coin数据的结果:

// 在你的DAO中新增以下方法
@Transaction
@Query("SELECT * FROM coinRevenue ORDER BY rank")
fun getAllCoinRevenueWithCoin(): Flowable<List<CoinRevenueWithCoin>>

// 如果需要根据CoinId查询单个关联结果
@Transaction
@Query("SELECT * FROM coinRevenue WHERE coinId = :coinId")
fun getCoinRevenueWithCoin(coinId: String): Maybe<CoinRevenueWithCoin>

第三步:业务层使用方式

现在你就可以用新的DAO方法来获取数据了,而且当你更新Coin的价格后,再次查询拿到的CoinRevenueWithCoin里的coin对象一定是最新的——因为每次查询都是从Coin表实时拉取数据,而不是存在CoinRevenue表的旧数据:

举个例子:

  1. 插入EUR的Coin:
    insertCoin(Coin(id="eur_123", symbol="EUR", priceInUsd=1.0253f))
    
  2. 插入关联的CoinRevenue:
    insertCoinRevenue(CoinRevenue(coinId="eur_123", amount=100f))
    
  3. 更新EUR的价格:
    insertCoin(Coin(id="eur_123", symbol="EUR", priceInUsd=2.522f))
    
  4. 获取关联数据:
    getAllCoinRevenueWithCoin().subscribe { list ->
        list.firstOrNull()?.let { item ->
            // 这里item.coin.priceInUsd就是最新的2.522f
        }
    }
    

为什么这是最佳方案?

  • 数据一致性:Coin和CoinRevenue分表存储,更新Coin时不需要修改CoinRevenue表,查询时实时关联,彻底避免数据冗余和不一致的问题。
  • 符合Room规范:用外键保证关联的完整性,@Transaction保证查询的原子性,@Relation由Room自动处理关联逻辑,还会优化查询性能。
  • 完美适配一对一需求:虽然Room文档里@Relation示例多用集合,但一对一场景下直接返回单个实体是完全支持的,如果找不到关联的Coin,coin字段会是null,你可以根据业务需求处理这种情况。

额外注意事项

  • 确保Coin的id是唯一且稳定的,因为外键完全依赖这个字段来建立关联。
  • 如果你用Flowable来监听数据变化,当Coin或CoinRevenue表有任何更新时,Flowable会自动发射最新的数据,完美契合你要的实时同步需求。

内容的提问来源于stack exchange,提问作者Shahar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:04:53