<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
Room 是 Google 官方推出的資料庫 ORM 框架。ORM 是指 Object Relational Mapping
,即物件關係對映,也就是將關係型資料庫對映為物件導向的語言。使用 ORM 框架,我們就可以用物件導向的思想操作關係型資料庫,不再需要編寫 SQL 語句。
apply plugin: 'kotlin-kapt' dependencies { ... implementation 'androidx.room:room-runtime:2.2.5' kapt 'androidx.room:room-compiler:2.2.5' }
Room 的使用可以分為三步:
建立 Entity 類:也就是實體類,每個實體類都會生成一個對應的表,每個欄位都會生成對應的一列。
建立 Dao 類:Dao 是指 Data Access Object
,即資料存取物件,通常我們會在這裡封裝對資料庫的增刪改查操作,這樣的話,邏輯層就不需要和資料庫打交道了,只需要使用 Dao 類即可。
建立 Database 類:定義資料庫的版本,資料庫中包含的表、包含的 Dao 類,以及資料庫升級邏輯。
新建一個 User 類,並新增 @Entity
註解,使 Room 為此類自動建立一個表。在主鍵上新增 @PrimaryKey(autoGenerate = true)
註解,使得 id 自增,不妨將這裡的主鍵 id 記作固定寫法。
@Entity data class User(var firstName: String, var lastName: String, var age: Int) { @PrimaryKey(autoGenerate = true) var id: Long = 0 }
建立一個介面類 UserDao,並在此類上新增 @Dao
註解。增刪改查方法分別新增 @Insert
、@Delete
、@Update
、@Query
註解,其中,@Query
需要編寫 SQL 語句才能實現查詢。Room 會自動為我們生成這些資料庫操作方法。
@Dao interface UserDao { @Insert fun insertUser(user: User): Long @Update fun updateUser(newUser: User) @Query("select * from user") fun loadAllUsers(): List<User> @Query("select * from User where age > :age") fun loadUsersOlderThan(age: Int): List<User> @Delete fun deleteUser(user: User) @Query("delete from User where lastName = :lastName") fun deleteUserByLastName(lastName: String): Int }
@Query
方法不僅限於查詢,還可以編寫我們自定義的 SQL 語句,所以可以用它來執行特殊的 SQL 操作,如上例中的 deleteUserByLastName
方法所示。
新建 AppDatabase 類,繼承自 RoomDatabase
類,新增 @Database
註解,在其中宣告版本號,包含的實體類。並在抽象類中宣告獲取 Dao 類的抽象方法。
@Database(version = 1, entities = [User::class]) abstract class AppDatabase : RoomDatabase() { abstract fun userDao(): UserDao companion object { private var instance: AppDatabase? = null @Synchronized fun getDatabase(context: Context): AppDatabase { return instance?.let { it } ?: Room.databaseBuilder(context.applicationContext, AppDatabase::class.java, "app_database") .build() .apply { instance = this } } } }
在 getDatabase 方法中,第一個引數一定要使用 applicationContext
,以防止記憶體漏失,第三個參數列示資料庫的名字。
佈局中只有四個 id 為 btnAdd,btnDelete,btnUpdate,btnQuery 的按鈕,故不再給出佈局程式碼。
MainActivity 程式碼如下:
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val userDao = AppDatabase.getDatabase(this).userDao() val teacher = User("lin", "guo", 66) val student = User("alpinist", "wang", 3) btnAdd.setOnClickListener { thread { teacher.id = userDao.insertUser(teacher) student.id = userDao.insertUser(student) } } btnDelete.setOnClickListener { thread { userDao.deleteUser(student) } } btnUpdate.setOnClickListener { thread { teacher.age = 666 userDao.updateUser(teacher) } } btnQuery.setOnClickListener { thread { Log.d("~~~", "${userDao.loadAllUsers()}") } } } }
每一步操作我們都開啟了一個新執行緒來操作,這是由於資料庫操作涉及到 IO,所以不推薦在主執行緒執行。在開發環境中,我們也可以通過 allowMainThreadQueries()
方法允許主執行緒運算元據庫,但一定不要在正式環境使用此方法。
Room.databaseBuilder(context.applicationContext, AppDatabase::class.java, "app_database") .allowMainThreadQueries() .build()
點選 btnAdd,再點選 btnQuery,Log 如下:
~~~: [User(firstName=lin, lastName=guo, age=66), User(firstName=alpinist, lastName=wang, age=3)]
點選 btnDelete,再點選 btnQuery,Log 如下:
~~~: [User(firstName=lin, lastName=guo, age=66)]
點選 btnUpdate,再點選 btnQuery,Log 如下:
~~~: [User(firstName=lin, lastName=guo, age=666)]
由此可見,我們的增刪改查操作都成功了。
使用 fallbackToDestructiveMigration()
可以簡單粗暴的升級,也就是直接丟棄舊版本資料庫,然後建立最新的資料庫
Room.databaseBuilder(context.applicationContext, AppDatabase::class.java, "app_database") .fallbackToDestructiveMigration() .build()
注:此方法過於暴力,開發階段可使用,不可在正式環境中使用,因為會導致舊版本資料庫丟失。
建立 Entity 類
@Entity data class Book(var name: String, var pages: Int) { @PrimaryKey(autoGenerate = true) var id: Long = 0 }
建立 Dao 類
@Dao interface BookDao { @Insert fun insertBook(book: Book) @Query("select * from Book") fun loadAllBooks(): List<Book> }
修改 Database 類:
@Database(version = 2, entities = [User::class, Book::class]) abstract class AppDatabase : RoomDatabase() { abstract fun userDao(): UserDao abstract fun bookDao(): BookDao companion object { private var instance: AppDatabase? = null private val MIGRATION_1_2 = object : Migration(1, 2) { override fun migrate(database: SupportSQLiteDatabase) { database.execSQL( """ create table Book ( id integer primary key autoincrement not null, name text not null, pages integer not null) """.trimIndent() ) } } @Synchronized fun getDatabase(context: Context): AppDatabase { return instance?.let { it } ?: Room.databaseBuilder(context.applicationContext, AppDatabase::class.java, "app_database") .addMigrations(MIGRATION_1_2) .build() .apply { instance = this } } } }
注:這裡的修改有:
Migration
物件,並將其新增到 getDatabase 的 builder 中現在如果再運算元據庫,就會新增一張 Book 表了。
比如在 Book 中新增 author 欄位
@Entity data class Book(var name: String, var pages: Int, var author: String) { @PrimaryKey(autoGenerate = true) var id: Long = 0 }
修改 Database,增加版本 2 到 3 的遷移邏輯:
@Database(version = 3, entities = [User::class, Book::class]) abstract class AppDatabase : RoomDatabase() { abstract fun userDao(): UserDao abstract fun bookDao(): BookDao companion object { private var instance: AppDatabase? = null private val MIGRATION_1_2 = object : Migration(1, 2) { override fun migrate(database: SupportSQLiteDatabase) { database.execSQL( """ create table Book ( id integer primary key autoincrement not null, name text not null, pages integer not null) """.trimIndent() ) } } private val MIGRATION_2_3 = object : Migration(2, 3) { override fun migrate(database: SupportSQLiteDatabase) { database.execSQL( """ alter table Book add column author text not null default "unknown" """.trimIndent() ) } } @Synchronized fun getDatabase(context: Context): AppDatabase { return instance?.let { it } ?: Room.databaseBuilder(context.applicationContext, AppDatabase::class.java, "app_database") .addMigrations(MIGRATION_1_2, MIGRATION_2_3) .build() .apply { instance = this } } } }
注:這裡的修改有:
version 升級建立 Migration
物件,並將其新增到 getDatabase 的 builder 中
修改 MainActivity:
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val bookDao = AppDatabase.getDatabase(this).bookDao() btnAdd.setOnClickListener { thread { bookDao.insertBook(Book("第一行程式碼", 666, "guolin")) } } btnQuery.setOnClickListener { thread { Log.d("~~~", "${bookDao.loadAllBooks()}") } } } }
點選 btnAdd,再點選 btnQuery,Log 如下:
~~~: [Book(name=第一行程式碼, pages=666, author=guolin)]
這就說明我們對資料庫的兩次升級都成功了。
參考文章
《第一行程式碼》(第三版)- 第 13 章 13.5 Room
以上就是Android Jetpack元件Room用例講解的詳細內容,更多關於Android Jetpack元件Room的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45