今天要聊的是在Room Database 底下一種名為資料表關聯(relationship)的實作
其實當初我會想做這個,主要是因為我純粹想了解要如何實作「多重資料表」...也就是如何在Room的框架下,在一個資料庫底下建立多個資料表的方式
然後循線一直查著查著...就開始研究到這個關鍵字-資料表關聯(relationship)了
那到底什麼是資料表關聯呢?我們來是想一個情境圖
首先,我們要製作一個「寵物登記系統」
那,我所設計的系統如下
如圖,可以看到我們的設定為一位主人會擁有多隻寵物
根據官方給出的定義,這是屬於one to many的一個架構
(網址在這: https://medium.com/androiddevelopers/database-relations-with-room-544ab95e4542)
因此,今天的實作中,我們會把主人(Master)與寵物pet分成兩個資料表,如下圖
那主人的資料表中,結構如下
一共有五位主人,他們在申請成為主人時我設定系統會授與它一組UUID
然後是寵物的資料表
寵物的資料表中,除了基本的ID之外,這張資料表亦會登記寵物名字與主人UUID
因此最後若是想調出該寵物相對應的主人的資料的話,便就是使用主人的UUID去搜尋主人並調出資料
okay,大致講解完今天要實作的東西了,那我們就來看實作了
來吧,上範例
&Github
->https://github.com/thumbb13555/RoomRelationshipsDemo
1. 載入庫及設置介面
先看一下這次的資料結構
然後,載入這次所有需要的庫
請切到build.gradle(Module)
在dependencies輸入以下
dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation 'androidx.core:core-ktx:1.6.0' implementation 'androidx.appcompat:appcompat:1.3.0' implementation 'com.google.android.material:material:1.3.0' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' testImplementation 'junit:junit:4.+' androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' kapt 'androidx.room:room-compiler:2.4.0-alpha03' implementation 'com.facebook.stetho:stetho:1.5.1' implementation 'androidx.room:room-runtime:2.4.0-alpha03' }
Sync Now 完成編譯
介面也沒有什麼,直接給
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <TextView android:id="@+id/textView_Result" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" android:textAlignment="center" android:textSize="16sp" android:textStyle="bold" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> <Spinner android:id="@+id/spinner_Select" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="32dp" android:layout_marginEnd="32dp" app:layout_constraintBottom_toTopOf="@+id/textView_Result" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
至此,準備工作已完成40%~
2. 建立Room
關於Room資料庫建立在這裡有寫,可以先參考此處
->碼農日常-『Android studio』Room資料庫建立與操作(Kotlin)
所以我就不再多廢話,僅PO程式
首先建立實體類
@Entity data class Master( @PrimaryKey var id:String, var name:String, var phone:String ) @Entity( foreignKeys = [ForeignKey( entity = Master::class, parentColumns = arrayOf("id"), childColumns = arrayOf("masterId"), onDelete = ForeignKey.CASCADE )] ) data class Pet( @PrimaryKey (autoGenerate = true) var petId:Int,//一般數量1~10 @ColumnInfo(name = "masterId") var masterId:String,//等於UUID var petName:String )
這地方跟以往的資料表不同
之前的資料表通常就只有一個,所以一個實體類就可以搞定了
但是這裡不但有兩個,而且還要有關聯性
所以@Entity就有兩個,而且在第二個Entity下還得建立與主資料表之間的關連
再來建立資料庫操作實體類
@Database(entities = [Master::class, Pet::class], version = 1) abstract class RoomDB : RoomDatabase() { abstract fun dao(): Dao? companion object { private var INSTANCE: RoomDB? = null fun getAppDatabase(context: Context): RoomDB? { if (INSTANCE == null) { INSTANCE = Room.databaseBuilder( context.applicationContext, RoomDB::class.java, "MasterDB" ) .allowMainThreadQueries() .build() } return INSTANCE } } }
特別注意一下粉底白字位置
粉底白字那邊就是載入多個資料表的方式,所以若是需求為僅是需要多個資料表的話,那只需這樣加入即可!
@Dao interface Dao { @Insert fun insertMaster(master: Master): Long @Insert fun insertPet(pet: Pet) @Query("Select * From master") fun getMasterList():List<Master> @Query("Select * From Master inner join Pet on Master.id = Pet.masterId where Master.id = :id") fun getMasterWithPets(id:String):List<MasterWithAllPets>? }
這裡一樣沒有什麼,就是跟一般呼叫Room指令一樣
不過紅字部分會報錯,因為我們還沒寫關聯
因此接下來要寫關聯囉:D
3. 撰寫資料表關聯
雖然上段講得很偉大XD
不過其實根本就沒有什麼,簡單來說就是把在Master.kt中已定義好的entityColumn給關聯起來
因此,程式如下
class MasterWithAllPets ( @Embedded var master: Master, @Relation (parentColumn = "id",entityColumn = "masterId") var pets:List<Pet> )
okay,到此為止已經把所有的準備工作完成了
而剛才報錯的地方也應該恢復了
接下來就是最後一步驟,資料庫操作囉
4. MainActivity 資料操作
最後都是資料庫操作的部分,一樣先全PO
class MainActivity : AppCompatActivity() { val TAG = MainActivity::class.java.simpleName + "My" override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) /**開啟Stetho資料庫監視*/ Stetho.initializeWithDefaults(this) Thread { val dao = RoomDB.getAppDatabase(applicationContext)?.dao() //如果列表沒有任何數據,則新增 if (dao!!.getMasterList().isEmpty()) { inputMasterWithPetData() } else { //載入主人與寵物資訊 initMaster(dao) } }.start() /**選擇欲查詢之主人資訊*/ spinner_Select.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) { Thread { val dao = RoomDB.getAppDatabase(applicationContext)?.dao() searchInfo(dao!!, p2) }.start() } override fun onNothingSelected(p0: AdapterView<*>?) {} } } /**載入主人資訊*/ private fun initMaster(dao: Dao) { val allMaster = dao.getMasterList() val masterArray = ArrayList<String>() allMaster.forEach { Log.d(TAG, "所有的主人名單: ${it.name}, id: ${it.id}"); masterArray.add(it.name) } val adapter = ArrayAdapter(this, android.R.layout.simple_dropdown_item_1line, masterArray) runOnUiThread { spinner_Select.adapter = adapter } } /**搜尋該主人所飼養的寵物*/ private fun searchInfo(dao: Dao, id: Int) { val allMaster = dao.getMasterList() val list = dao.getMasterWithPets(allMaster[id].id) val masterInfo = "主人${list!![0].master.name} 的寶貝們為: " Log.d(TAG, masterInfo); val string = StringBuffer(masterInfo + "\n") val pets = list[0].pets pets.forEach { val pet = "寵物名字: ${it.petName}" Log.d(TAG, pet) string.append(pet + "\n") } runOnUiThread { textView_Result.text = string.toString() } } /**手動增加測試資料*/ private fun inputMasterWithPetData() { val dao = RoomDB.getAppDatabase(applicationContext)?.dao() val nameList = arrayListOf("Jack", "Noah", "Sam", "Tilly", "ShiYan") val phoneList = arrayListOf("091122334455", "0936589745", "0956842398", "038569741", "0988556412") val petList: Array<Array<String>> = arrayOf( arrayOf("小黑", "小白", "小黃"), arrayOf("小扁", "阿飛"), arrayOf("貓", "狗", "兔兔", "鳥"), arrayOf("老王"), arrayOf("大哈", "二哈"), ) for (i in 0 until nameList.size) { val uuid = UUID.randomUUID().toString() val master = Master(uuid, nameList[i], phoneList[i]) dao?.insertMaster(master) petList[i].forEach { val pet = Pet(0, uuid, it) dao?.insertPet(pet) } } //載入主人資訊 initMaster(dao!!) } }
分解一下重點
我也有標記logcat輸出,也可以觀察一下
ok,基本上所有程式就到這邊囉
Emmm我後面之所以沒再多解釋程式,是因為我覺得會搜尋到這篇的有很大的可能是本身就會寫Room的人
如果你搜到這篇但沒寫過Room,那再次建議你先看我這篇文章實作一次歐~
->碼農日常-『Android studio』Room資料庫建立與操作(Kotlin)
啊...超沒幹勁,超忙的啊這週...
我最近在寫一個APP,詳細在這裡
->【LikeCoin Grants Lite】LikeCoin 餘額即時顯示小工具(Android)
雖然忙,但是週更還是不能馬虎啊(笑)
這個APP我做出來是打算用來給大家查詢自己餘額的,也就是
碼農日常-『Android studio』AppWidgetProvider桌面小工具開發
所講到的APP
這個作品也預計會上架,敬請期待吧XD
留言列表