Roomで1対1対応のリレーションを実現したい
Roomを使ったアプリケーションで、別々のテーブルの情報が両方必要なので、昔Railsを触った自分としては、
「ついにAndroidのRoomでもリレーションを実現しなければならない時が来た」
と思いました。
そこでドキュメントなど、色々見ていたのですがRoomで1対1対応のリレーションは、ズバリこう実装すれば良いというのがイマイチ分からないでいました。
ActiveRecordでいうhas_oneのような物は無いのでしょうか。
ところが「参照するだけなら、そもそもRoomでリレーション組まなくても実装できる」という話を聞いて、自分は最初よく理解出来ませんでした。
DBでリレーション使わず、実現した方法
今回はサンプルとして、仮にゲームのQuestionテーブルとScoreテーブルとします。
// データモデルQuestion
id: Int
level: Int
number: Int
question: String
created_at: String
// データモデルScore
id: Int
level: Int
number: Int
time: Int
score: Int
clear_date: String
この2つを関連づけたいとします。このQuestionテーブルに対応したScoreを結び付けたい。必要な情報を抽出して、
// QuestionとScoreをまとめて扱う、QuestionAndScoreクラス
class QuestionAndScore {
companion object {
fun createQuestionAndScore(level: Int, number: Int, question: String): QuestionAndScore {
return QuestionAndScore().apply {
this.level = level
this.number = number
this.question = question
}
}
}
var level: Int = 0
var number: Int = 0
var question: String = ""
var time: String = ""
}
2つのテーブルの情報を同時に扱うQuestionAndScoreクラスを作成しました。
- 今回はQuestion情報に対する最高スコアを組み合わせた問題リストを作成することにします。
val allQuestions = getAllQuestions() //全ての問題データを取得する
val allScores = getAllScores() //全てのスコアデータ取得
// 問題データとスコアデータを組み合わせたリスト
val allQuestionsAndScores = mutableListOf<QuestionAndScore>().apply {
allQuestions.forEach { questionData ->
val level = questionData.level
val number = questionData.number
val question = questionData.question
val questionAndScore = createQuestionAndScore(level, number, questoin)
// 関連するスコアだけを取り出す
val scoreList = allScores.filter { scoreData ->
scoreData.level == level && scoreData.number == number
}
// 最高スコアを取得
val maxScore = scoreList.maxby {
it.score
}!!.score
questionAndScore.score = maxTime
add(questionAndScore)
}
}
これで問題情報と各問題の最高スコアを組み合わせた問題リストが作成出来ました。
値を参照するだけなら、このように実現することが出来ました。