有些資料不需連網,只需存在手機的話
可在手機上建立資料庫
主要包含的有三部分
定義表的結構,建立一個data class
每個Entity類似試算表中的每個row(horizontal)
其中包含各種屬性column(vertical)表格欄位
同個class全部的Entity組成就像是一份資料表
例如有一份睡眠統計資料,首先建立一個SleepNight.kt
data class SleepNight(
var nightId : Long = 0L,
val startTimeMilli : Long = System.currentTimeMillis(),
var endTimeMilli : Long = startTimeMilli,
var sleepQuality : Int = -1
)
加入相關的註解,做成Entity如下
//註解表示這是一份@Entity,並且使用參數tableName命名爲"daily_sleep_quality_table"
@Entity(tableName = "daily_sleep_quality_table")
data class SleepNight(
//每個資料都必須有個唯一值用於識別
//參數autoGenerate使Room自動爲每個Entity產生
@PrimaryKey(autoGenerate = true)
var nightId: Long = 0L,
//以下定義Entity的各屬性
@ColumnInfo(name = "start_time_milli")
val startTimeMilli: Long = System.currentTimeMillis(),
@ColumnInfo(name = "end_time_milli")
var endTimeMilli: Long = startTimeMilli,
@ColumnInfo(name = "quality_rating")
var sleepQuality: Int = -1
)
data access object
建立用來操作資料庫的interface
提供例如@Query
,@Insert
,@Delete
,@Update
等annotation定義操作資料庫
如使用@Insert
,@Update
時Room會產生所需的對應方法
使用其它annotation時,編譯器也會協助檢查SQL語法
新增一個SleepDatabaseDao.kt,在宣告interface前加入@Dao
@Dao
interface SleepDataDao{}
然後在此interface中加入相對功能的annotation
@Dao
interface SleepDatabaseDao {
@Insert
fun insert(night: SleepNight)
@Update
fun update(night: SleepNight)
//從daily_sleep_quality_table查詢,nightId符合get()中的key
@Query("SELECT * FROM daily_sleep_quality_table WHERE nightId = :key")
fun get(key: Long): SleepNight?
//清除全部資料表
@Query("DELETE FROM daily_sleep_quality_table")
fun clear()
//依nightId將資料表做降序(descending大到小),LIMIT 1表示只回傳一個項目
//getTonight()回傳可空的SleepNight,因爲資料表在一開始與全部清除時,都會是空的
@Query("SELECT * FROM daily_sleep_quality_table ORDER BY nightId DESC LIMIT 1")
fun getTonight(): SleepNight?
@Query("SELECT * FROM daily_sleep_quality_table ORDER BY nightId DESC")
fun getAllNights(): LiveData<List<SleepNight>>
}
清除還可使用@Delete
,但主要用於知道要清除資料表中的某個內容
如果要清除整個資料表的內容,還是使用@Query
比較有效率
建立爲abstract class的資料庫以存放data class與DAO
一般程序爲:
public abstract
class that extends RoomDatabase
. This class is to act as a database holder. The class is abstract, because Room
creates the implementation for you.@Database
. In the arguments, declare the entities for the database and set the version number.companion object
, define an abstract method or property that returns a SleepDatabaseDao
. Room
will generate the body for you.Room
database for the whole app, so make the RoomDatabase
a singleton.Room
's database builder to create the database only if the database doesn't exist. Otherwise, return the existing database.@Database()
abstract class SleepDatabase : RoomDatabase() {}
@Database
須要相關的參數於()中
@Database(entities = [SleepNight::class], version = 1, exportSchema = false)
abstract class SleepDatabase : RoomDatabase() {
abstract val sleepDatabaseDao: SleepDatabaseDao
}
在database中建立一個companion object
,並宣告一個可空變數INSTANCE
此變數會固定保存對資料庫的參考,可避免重複連結資料庫而降低效能
而以Volatile註解表示此INSTANCE變數僅會在主記憶體中讀寫
可避免進入cache被不同執行緒讀寫而造成問題
@Volatile
private var INSTANCE: SleepDatabase? = null
接着再定義一個getInstance(ontext: Context)
方法,在此方法中加入synchronized{}區塊,並透過this參數以存取這個context
利用synchronized{}區塊,表示一次僅可一個執行緒進入此區塊初始化資料庫
可避免若有多個執行緒同時要建立資料庫的情況
在synchronized{}內宣告一變數var instance = INSTANCE
指向INSTANCE爲僅在此使用的方式
接着判斷instance是否null,若爲null則使用Room.databaseBuilder建立資料庫
(範例呼叫的fallbackToDestructiveMigration()表示破壞與重建資料庫)
abstract val sleepDatabaseDao: SleepDatabaseDao
companion object {
@Volatile
private var INSTANCE: SleepDatabase? = null
fun getInstance(context: Context): SleepDatabase {
synchronized(this) {
var instance = INSTANCE
if (instance == null) {
instance = Room.databaseBuilder(
context.applicationContext,
SleepDatabase::class.java,
"sleep_history_database"
)
.fallbackToDestructiveMigration()
.build()
// 如果INSTANCE是空,透過instance建立之後,再丟給INSTANCE
INSTANCE = instance
}
return instance
}
}
}
最後return intstance
前面已經完成資料庫的建置步驟
現在使用測試檢視資料庫以確保有置成功
@RunWith(AndroidJUnit4::class)
註解表示以下開始測試程序@Before
註解的createDb()函數表示先在記憶體中建立資料庫,這只是暫時性的,並未真的建立在裝置的檔案系統中,測試結束後會從記憶體清除@Test
註解的函數中,新增,插入SleepNight的entity@After
註解的函數關閉資料庫@RunWith(AndroidJUnit4::class)
class SleepDatabaseTest {
private lateinit var sleepDao: SleepDatabaseDao
private lateinit var db: SleepDatabase
@Before
fun createDb() {
val context = InstrumentationRegistry.getInstrumentation().targetContext
// Using an in-memory database because the information stored here disappears when the
// process is killed.
db = Room.inMemoryDatabaseBuilder(context, SleepDatabase::class.java)
// Allowing main thread queries, just for testing.
.allowMainThreadQueries()
.build()
sleepDao = db.sleepDatabaseDao
}
@After
@Throws(IOException::class)
fun closeDb() {
db.close()
}
@Test
@Throws(Exception::class)
fun insertAndGetNight() {
val night = SleepNight()
sleepDao.insert(night)
val tonight = sleepDao.getTonight()
assertEquals(tonight?.sleepQuality, -1)
}
}
最後在此測試的test.kt檔按右鍵run執行,檢視是否通過
https://enginebai.com/2019/04/03/android-database-room/