web-dev-qa-db-ja.com

1対1の関係を持つルームデータベース

CoinとCoinRevenueという2つのエンティティがあります。

基本的に、コインは他の通貨の価格を米ドルで保持しています。

たとえば、1.0356の値を持つシンボルEURのコイン

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

CoinRevenueは、特定のコインのうちユーザーが持っているコインの量を保持するために使用するエンティティです。たとえば、CoinRevenueは、通貨記号が1000のCoin Entityと関係があります。

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

ここで、データベースからCoinRevenueを取得し、データベースから更新されたCoinを取得します。

たとえば、(EUR、1.0253)でCoinを保存し、そのコインでCoinRevenueを保存しました。

その後、(EUR、2.522)でCoinを更新しましたCoinRevenue内のCoinオブジェクトも更新されます。

@Embeddedは、同じ親オブジェクトに列として内部オブジェクトフィールドを追加するだけであることを理解しています。リレーションを使用するときは、リストまたはセットを使用する必要があります。ただし、CoinRevenue内には常に1つのコインがあります。

私のcoinDAO:

@Query("select * from coin order by rank")
fun getAllCoins(): Flowable<List<CoinDB>>

@Query("select * from coin where rank = 1")
fun getFirstCoin(): Maybe<CoinDB>

@Query("select * from coin where favourite = 1 order by rank")
fun getAllFavouriteCoins(): Flowable<List<CoinDB>>

@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertCoin(coinDB: CoinDB)

@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertCoins(coinsList: List<CoinDB>)

// -----------------
// CoinRevenue
// -----------------

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

これを作成する最良の方法は何ですか?

11
Shahar

だから、何度も試してみて、なんとか動作するようになりました。

CoinRevenueオブジェクトを変更して、Coin IDへの外部キーを保持しました

@Entity(tableName = "coinRevenue", foreignKeys = (arrayOf(ForeignKey(
        entity = CoinDB::class,
        onUpdate = ForeignKey.CASCADE,
        parentColumns = arrayOf("coinId"),
        childColumns = arrayOf("coinDbId"))))
)
data class CoinRevenue(
        @ColumnInfo(name = "mid")
        @PrimaryKey(autoGenerate = true)
        var id: Long = 0L,
        @ColumnInfo(name = "coinDbId")
        var coinDbId: String? = null,
        @ColumnInfo(name = "amount")
        var amount: Double = 0.toDouble()
)

次のように、両方のオブジェクトでPOJOを作成する必要がありました。

class CoinRevenueWithCoin() : Parcelable {
@Embedded lateinit var coinDB: CoinDB
@Embedded lateinit var coinRevenue: CoinRevenue
}

クエリは次のようになります:

@Query("select * from coinRevenue, coin where coinRevenue.coinDbId = coin.coinId order by coin.rank")
fun getAllCoinsRevenueWithCoin(): Flowable<List<CoinRevenueWithCoin>>

それでおしまい。

さらに、このクエリは、他の通常のオブジェクトクエリと同様に、「coin」テーブルまたは「coinRevenue」テーブルに変更があった場合にオブジェクトを発行します

12
Shahar

ソリューションにはいくつかの大きな欠点があります。その1つは、テーブルの列に異なる名前を付ける必要があることです。 @embedededを使用する代わりに、@ Relationを適用することをお勧めします。

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

@Entity(tableName = "coinRevenue", foreignKeys = (arrayOf(ForeignKey(
        entity = CoinDB::class,
        onUpdate = ForeignKey.CASCADE,
        parentColumns = arrayOf("coinId"),
        childColumns = arrayOf("coinDbId"))))
)
data class CoinRevenue(
        @ColumnInfo(name = "mid")
        @PrimaryKey(autoGenerate = true)
        var id: Long = 0L,
        @ColumnInfo(name = "coinDbId")
        var coinDbId: String? = null,
        @ColumnInfo(name = "amount")
        var amount: Double = 0.toDouble()
) 

私はKotlinに精通していないので、ソリューションはJavaにあります

class CoinRevenueExt extends CoinRevenue {
        @Relation(parentColumn = "coinDbId", entityColumn = "coinId" ) 
        List<Coin> coins;

        public Coin getCoin() {
            return coins.get(0);
        }

}

Daoはそのようにシンプルです

@Query("select * from coinRevenue")
public Flowable<List<CoinRevenueExt>> getAllCoinsRevenueWithCoin();
5
sim

あなたが本当に達成しようとしていることを伝えるのは少し難しいです。また、あなたの命名は少し奇妙です。コイン表には実際に通貨情報が含まれているようです。 coinRevenueNewは、元帳のエントリまたは注文です。あなたが例に従うほうが簡単なら、より多くの人があなたの投稿を読み終えようとします。

また、解決しようとしている問題は少し不明瞭です。 -あなたの問題はデータベースでそれをモデリングしていますか? -通貨が変更されたときにすべての金額を自動的に更新したいという問題がありますか? -データベースが変更されたときに、メモリ内のオブジェクトを更新したいという問題がありますか? -ルームで外部キーを使用することに問題がありますか?

モデリングの最初の問題は、他の人々によって示唆されています。外部キーを使用します。それについての記事がたくさんあります。

もう少し理解しやすいドメイン名を使用すると、次のような2つのテーブルができます。

create table currency (id integer primary key, exchange_rate float);
create table order (id integer primary key, int total, int currency_id, foreign key(currency_id) references currency(id));

それぞれに部屋エンティティを作成します。両方を組み合わせた3つ目のクラスを作成します(@Entityでマークしないでください)。ここでアノテーション@Embeddedまたは@Relationを使用できます。ドキュメントではこれについてさらに説明しています。

https://developer.Android.com/reference/Android/Arch/persistence/room/Relation.html

通貨を変更した場合、ストレージシステムはすべての注文合計を自動的に更新しません。 「total_in_foreign_currency」のフィールドと「total_in_master_currency」のフィールドがある場合、データベースは再計算されません。各行を手動で繰り返し、再計算する必要があります。

メモリ内のデータオブジェクトは魔法のように更新されません。データを取得したときと、データがまだ残っているかどうかを追跡する必要があります。 LiveDataを使用して、データが変更されるたびに通知を受けることができます(ただし、オブジェクトを魔法のように更新することはありません)。

0
Thomas Fischer