はじめに
SQLite のラッパーライブラリである Room を利用して、あるテーブルからビューを生成してみたいと思います。今回は 2つのテーブルから、1つのビューを生成しようと思います。次のような User と Repo という 2つのテーブルを作成し、そこから UserRepo という 1つのビューを生成する感じのものです。
※ ビューを生成するだけために UserId を主キーとする User と Repo のテーブルを定義してます。内容に関しては意味があるわけではありません。
準備
Roomのセットアップ方法は[Android]Roomを使ったサンプルと解説 で紹介していますので参考にしてセットアップします。
実装
User テーブルを作成する
User 情報のためのテーブルを users というテーブル名称で作成します。 User は主キーとして ID を持ち、その他に FirstName や LastName を持つユーザー情報になります。
User.kt
@Entity(tableName = "users")
data class User(
@PrimaryKey(autoGenerate = false) val id: Int,
val firstName: String?,
val lastName: String?
)
テーブルの操作には Dao が必要になります。なので Insert, Update, Delete, DeleteAll, GetAll を備えた標準的な Dao を定義します。
UserDao.kt
@Dao
interface UserDao {
@Insert
fun insert(user : User)
@Update
fun update(user : User)
@Delete
fun delete(user : User)
@Query("delete from users")
fun deleteAll()
@Query("select * from users")
fun getAll(): List<User>
@Query("select * from users where id = :id")
fun getUser(id: Int): User
}
Repo テーブルを作成する
Repo 情報のためのテーブルを repos というテーブル名称で作成します。 Repo は主キーとして User のID を持ち、その他に Name を持つレポジトリ情報になります。
Repo
@Entity(tableName = "repos")
data class Repo(
@PrimaryKey val userId: Int,
val name: String?
)
テーブルの操作には Dao が必要になります。なので Insert, Update, Delete, DeleteAll, GetAll を備えた標準的な Dao を定義します。
RepoDao.kt
@Dao
interface RepoDao {
@Insert
fun insert(repo : Repo)
@Update
fun update(repo : Repo)
@Delete
fun delete(repo : Repo)
@Query("delete from repos")
fun deleteAll()
@Query("select * from repos")
fun getAll(): List<Repo>
@Query("select * from repos where userId = :userId")
fun getRepo(userId: Int): Repo
}
UserRepo ビューを生成する
users と repos テーブルから UserRepo ビューを作成していきます。Room でビューを作成するには @DatabaseView をつけ、viewName にはビュー名称、value にはどのようなビューを生成するのか記述した SQL 文を記述します。
SQL記述の際に気をつけること
- SQL 文に含めるデーブル名称は、各テーブルの定義で指定した名称にする
- SELECT で指定した属性名、クラスで定義したプロパティ名が一致しなければならない。
@DatabaseView(
viewName = "UserRepo",
value = """
SELECT users.id, users.lastName, users.firstName,
repos.name AS repoName FROM users
INNER JOIN repos ON users.id = repos.userId
"""
)
data class UserRepo(
val id: Int,
val lastName: String?,
val firstName: String?,
val repoName: String?
)
ビューを操作するにも Dao が必要になります。ビューを操作する Dao では
@Insert や @Delete、@Update などは使えません。そのため全て @Query で操作を記述する必要があります。今回は GetAll と GetUserRepo という単純な操作を定義しました。
@Dao
interface UserRepoDao {
@Query("select * from UserRepo")
fun getAll(): List<UserRepo>
@Query("select * from UserRepo where id = :id")
fun getUserRepo(id: Int): UserRepo
}
RoomDatabase を作成する
定義したテーブルとビューを操作するには RoomDatabase を実装したクラスが必要です。RoomDatabase の実装でやらなければいけないことは 2つです。
1つ目ですが、@Databse アノテーションをつける必要があります。@Database アノテーションを付け、entities には利用するテーブル、 views には利用するビューを指定してやります。
2つ目ですが テーブルとビューを操作する Dao を取得するメソッドを実装する必要があります。実装ですが簡単で Dao を戻り値とする関数を定義するだけです。
@Database(entities = [User::class, Repo::class], views = [UserRepo::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
abstract fun repoDao(): RepoDao
abstract fun userRepoDao(): UserRepoDao
}
これで RoomDatabase の定義が終わり、テーブルとビューを操作できるようになりました。
動作確認
実装が終わったのであとは動作確認してみましょう。定義した RoomDatabase を初期化し、それぞれのテーブルやビューの Dao を取得します。そして新たに User と Repo の情報をテーブルに追加した上で、UserRepo ビューから取得してみます。
class MainActivity : AppCompatActivity() {
private val database : AppDatabase by lazy {
Room.databaseBuilder(applicationContext, AppDatabase::class.java, "database-name"
).fallbackToDestructiveMigration().build()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
CoroutineScope(Dispatchers.IO).launch {
testForUserRepo()
}
}
private fun testForUserRepo() {
val userDao = database.userDao()
val repoDao = database.repoDao()
val userRepoDao = database.userRepoDao()
val newUser = User(0, Date().time.toString(), Date().time.toString())
val newRepo = Repo(newUser.id, Date().time.toString())
userDao.insert(newUser)
repoDao.insert(newRepo)
Log.v("TAG", "get view ${userRepoDao.getAll()}")
userDao.delete(newUser)
repoDao.delete(newRepo)
Log.v("TAG", "get view ${userRepoDao.getAll()}")
}
}
実行すると意図したとおり、 User と Repo があわさった UserRepo が取得できました。
V/TAG: get view [UserRepo(id=0, lastName=1589440137149, firstName=1589440137149, repoName=1589440137160)]
V/TAG: get view []
おわりに
Room でもビューは定義できました。説明した通りテーブルといくつか異なる点があるので注意が必要ですね。作成したコードは下記に保存しているので興味があれば閲覧してみてください。