添加依賴項
在專案層級的 build.gradle 檔案中,請在 ext 區塊中定義 room_version。
ext {
kotlin_version = "1.6.20"
nav_version = "2.4.1"
room_version = '2.4.2'
}
在應用程式層級的 build.gradle 檔案中,請於依附元件清單的結尾新增下列依附元件。
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"
// optional - Kotlin Extensions and Coroutines support for Room
implementation "androidx.room:room-ktx:$room_version"
需要加上 :
plugins {
...
id 'kotlin-kapt'
}
簡介
ORM (Object Relational Mapping、物件關聯對映)
- 將類別 (Classes) 對應到 資料表 (Tables)
- 將物件 (Objects) 對應到 資料表中的一筆記錄 (Rows in a table)
- 將屬性 (Attributes) 對應到資料表中的欄位 (Columns in a table)
實作
Entity 實體
在 Room 中,每個資料表會以一個類別表示,該類別在 Room 等 ORM 中,通常稱為「模型類別」或「實體」。系統會依此產生 SQLite 資料表。
在使用 Room 定義類別時,針對每個資料欄類型,只需考慮 Kotlin 類型,系統會自動將模型類別對應至資料庫使用的資料類型。
設計 Entity
- 新增 @ColumnInfo 註解來指定該欄的名稱。通常,與 Kotlin 屬性使用的 lowerCamelCase 不同,SQL 資料欄名稱的字詞以底線分隔。
- 在 SQL 中,根據預設,資料欄的值可以是空值,但您可以視需要另外將值標示為非空值。這不同於 Kotlin 的運作方式,在 Kotlin 中,根據預設,值不可為空值。
若不允許這個資料欄中的值為空值,請使用 @NonNull 註解加以標示。若非使用可空類型(比如String?),則即為不可空的,不必特別標註。 - 時間會在資料庫中以整數表示。這是 Unix 時間戳記,可轉換成可用日期。雖然不同版本的 SQL 都提供了轉換日期的方式,但您仍可按照自己的需要使用 Kotlin 日期格式設定函式。
- 可以選擇指定 @Entity(tableName="schedule"),但由於 Room 查詢不區分大小寫,因此可以不使用明確定義小寫的資料表名稱。
例子:
@Entity
data class Schedule (
@PrimaryKey val id: Int,
@ColumnInfo(name = "stop_name") val stopName: String,
@ColumnInfo(name = "arrival_time") val arrivalTime: Int
)
DAO
在 DAO 上呼叫函式相當於在資料庫上執行 SQL 指令。
DAO 函式就像您在此應用程式中定義的函式一樣,通常會指定 SQL 指令,以便您準確指定希望該函式執行的操作。
例子 :
@Dao
interface ScheduleDao {
@Query("SELECT * FROM schedule ORDER BY arrival_time ASC")
fun getAll(): List<Schedule>
@Query("SELECT * FROM schedule WHERE stop_name = :stopName ORDER BY arrival_time ASC")
fun getByStopName(stopName: String): List<Schedule>
}
使用 ViewModel 當 「檢視資料模型」
圖片來源 : 簡介 Room 與 Flow (android.com) |
例子 :
class BusScheduleViewModel(private val scheduleDao: ScheduleDao): ViewModel() {
fun fullSchedule(): List<Schedule> = scheduleDao.getAll()
fun scheduleForStopName(name: String): List<Schedule> = scheduleDao.getByStopName(name)
}
//由於 ViewModel 類別 BusScheduleViewModel 必須有生命週期感知特性,因此應以可回應生命週期事件的物件進行例項化。
//如果直接在其中一個片段中執行例項化,則片段物件必須處理一切 (包括所有記憶體管理工作),而這超出應用程式程式碼的工作範圍。
//您可以改為建立名為「工廠」的類別,該類別會替您將檢視畫面模型物件例項化。
class BusScheduleViewModelFactory(
private val scheduleDao: ScheduleDao
) : ViewModelProvider.Factory {
// 您可以使用 BusScheduleViewModelFactory.create() 對 BusScheduleViewModelFactory 物件執行個體化,
// 這樣一來,您的檢視畫面模型便可具有生命週期感知特性,而無需片段直接處理這項工作。
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(BusScheduleViewModel::class.java)) {
@Suppress("UNCHECKED_CAST")
return BusScheduleViewModel(scheduleDao) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
建立資料庫類別及預先填入資料庫
建立新的類別,負責 :
- 指定資料庫中定義的實體。
- 提供對各個 DAO 類別的單個執行個體的存取。
- 執行任何其他設定,例如預先填入資料庫。
例子 :
@Database(entities = [Schedule::class], version = 1)
abstract class AppDatabase: RoomDatabase() {
abstract fun scheduleDao(): ScheduleDao
companion object {
@Volatile
private var INSTANCE: AppDatabase? = null
//使用 Elvis 運算子傳回資料庫的現有執行個體 (如果已有執行個體),或視需求首次建立資料庫
fun getDatabase(context: Context): AppDatabase {
return INSTANCE ?: synchronized(this) {
val instance = Room.databaseBuilder(
context,
AppDatabase::class.java,
"app_database")
.createFromAsset("database/bus_schedule.db")
.build()
INSTANCE = instance
instance
}
}
}
}
使用它
建立新的類別如下 :
class BusScheduleApplication : Application() {
val database: AppDatabase by lazy { AppDatabase.getDatabase(this)}
}
並在 AndroidMainifest.xml 添加 :
<application
android:name="com.example.busschedule.BusScheduleApplication"
...
在 Fragment 中使用 ViewModel
private val viewModel: BusScheduleViewModel by activityViewModels {
BusScheduleViewModelFactory(
(activity?.application as BusScheduleApplication).database.scheduleDao()
)
}
0 comments:
張貼留言