Room 數(shù)據(jù)庫框架最全攻略

Room 數(shù)據(jù)庫框架最全攻略

RoomGoogle官方推出的Android Sqlite數(shù)據(jù)庫處理框架,是子啊Sqlite上提供了一個抽象層,以便在充分利用 SQLite 的強大功能的同時,能夠流暢地訪問數(shù)據(jù)庫。本文的目的旨在對于Room框架可以快速上手,內(nèi)容分為兩部分,第一部分為數(shù)據(jù)庫的基本操作(增、刪、改、查),第二部數(shù)據(jù)庫的升級,加密

image

基本使用

基本工作的準備

  • 引入Room依賴 (在Module的build.gradle文件中添加如下依賴)
plugins {
    id 'com.android.application'
    id 'kotlin-android'
    id 'kotlin-kapt'//在此處引入kapt插件
}

...

dependencies {
    ...
    
    def room_version = "2.2.6" // check latest version from docs
    implementation "androidx.room:room-runtime:$room_version"
    kapt "androidx.room:room-compiler:$room_version"
    
    ...
}



以上準備工作就做的差不多了,接下來開始建立數(shù)據(jù)庫表

  • 建表

    本文已 User 表為例

    @Entity
    data class User(
        @ColumnInfo(name = "first_name")
        val firstName: String,
        @ColumnInfo(name = "last_name")
        var lastName: String,
        @ColumnInfo(name = "age")
        val age: Int = 0,
         @PrimaryKey(autoGenerate = true)
        val uid: Int = 0
    )
    

    如上,一張User表就建好了,如果指定表名,默認是使用類名作為表明,如果要指定表名可以這樣:

    @Entity(tableName = "first_user")
    data class User(
        @ColumnInfo(name = "first_name")
        val firstName: String,
        @ColumnInfo(name = "last_name")
        var lastName: String,
        @ColumnInfo(name = "age")
        var age: Int = 0
      @PrimaryKey(autoGenerate = true)
        val uid: Int = 0
    )
    

    在上面的代碼中,一張first_user的表就建好了。里面有幾個注解需要說明一些

    • @ColumnInfo(用來指定數(shù)據(jù)庫表中對應的列明,屬性 name 可以單獨指定列名,如果不設置,默認為變量名作為列名)
    • @PrimaryKey (用來指定主鍵的,該注解修飾的字段屬性必須為 int ,long等整型,屬性autoGenerate 可以設置主鍵自增)

    在實際使用過中,以上寫法其實是有問題的,上面代碼指定了 uid做為主鍵,并且設置了自增,但是該屬性放在構(gòu)造方法的位置,書實例化User 這個對象的時候,這個 uid 的值是必須填寫的。所以要不指定主鍵,讓其自增,該如下實現(xiàn)

    @Entity(tableName = "first_user")
    data class User(
        @ColumnInfo(name = "first_name")
        val firstName: String,
        @ColumnInfo(name = "last_name")
        var lastName: String,
        @ColumnInfo(name = "age")
        var age: Int = 0
    ) {
        @PrimaryKey(autoGenerate = true)
        var uid: Int = 0
    
    }
    
  • Dao(包含用于訪問數(shù)據(jù)庫的方法)

    @Dao
    interface UserDao {
    }
    

    這個一個接口,包括了管理數(shù)據(jù)的方法,以 曾刪改查為例

    @Dao
    interface UserDao {
         @Query("SELECT * FROM user")
        fun getUser(): MutableList<User>//查詢所有
        
         @Delete
        fun delete(user: User)//刪除指定的User
        
         @Update(entity = User::class)
        fun updateUser(user: User)//修改指定的User
        
         @Insert
        fun insertUser(vararg users: User)//插入數(shù)據(jù),可以是單個,也可以是多個
    }
    
  • AppDatabase Dao管理對象

        @Database(entities = [User::class],version = 1)
        abstract class AppDatabase : RoomDatabase() {
            abstract fun userDao(): UserDao
            abstract fun carDao():CarDao
            ...
        }
        
    

    在以上代碼中有幾個注解簡單說下

    • @Database Marks a class as a RoomDatabase. 標記一個類作為 RoomDatabase,被這個注解修飾類是個抽象類,并且需要繼承 RoomDatabase,上面代碼就是該類的固定寫法
    • entities屬性,該屬性 修飾的是一個數(shù)組,該數(shù)組需要加入 被 @Entity 注解修飾的類,也就是數(shù)據(jù)庫中的表
    • version 屬性,該屬性制動了數(shù)據(jù)庫的版本
  • 創(chuàng)建數(shù)據(jù)庫實例

    創(chuàng)建上述文件后,使用以下代碼獲取已創(chuàng)建的數(shù)據(jù)庫的實例

        val db = Room.databaseBuilder(
                    applicationContext,
                    AppDatabase::class.java, "database-name"
                ).build()
        
    

    上述代碼已經(jīng)很清晰的展示了數(shù)據(jù)庫對象的創(chuàng)建過程,“database-name”,該字符串指定了數(shù)據(jù)庫的名稱,可以按需求改成自己設置的名稱

    注意:在單個進程中運行,在實例化 AppDatabase 對象時應遵循單例設計模式。每個RoomDatabase實例的成本相當高,如下


object DBHelper {
    val db = Room.databaseBuilder(
        App.context,
        AppDatabase::class.java,
        "snukaisens"
    ).build()
}
  • 測試

    
    class MainViewModel : ViewModel() {
        fun getUser(): MutableList<User> {
            return DBHelper.db.getUserDao().getUser()
        }
    
        fun insert(vararg users: User) {
            DBHelper.db.getUserDao().insertUser(*users)
        }
    
        fun findByName(firstName: String,lastName:String):User {
            return DBHelper.db.getUserDao().findByName(firstName, lastName)
        }
        fun updateUser(user: User) {
            DBHelper.db.getUserDao().updateUser(user)
        }
    }
    
    ...
    
    //需要說明的是,關于所有數(shù)據(jù)庫相關的操作,都需要在子線程中執(zhí)行
    GlobalScope.launch {
                val user = viewModel.getUser()//查詢
                println("第一次取值 = $user")
    
                val insertUser = User("Tom","Json")
                val insertUser1 = User("Tom1","Json1")
                val insertUser2 = User("Tom2","Json2")
                val insertUser3 = User("Tom3","Json3")
                val insertUser4 = User("Tom4","Json4")
    
                                      viewModel.insert(insertUser,insertUser1,insertUser2,insertUser3,insertUser4)//批量插入
                val user1 = viewModel.getUser()//再次查詢
                println(message = "第二次取值 = $user1")
    
                val onlyUser = viewModel.findByName("Tom4", "Json4")//按條件查詢
                onlyUser.lastName = "sunjian"
                viewModel.updateUser(onlyUser)//修改
            }
    
    

    執(zhí)行結(jié)果如下

    2021-03-12 15:16:18.211 30145-30269/com.example.firstapp I/System.out: 第一次取值 = []
    2021-03-12 15:16:18.213 30145-30269/com.example.firstapp I/System.out: 第二次取值 = [User(firstName=Tom, lastName=Json, age=0), User(firstName=Tom1, lastName=Json1, age=0), User(firstName=Tom2, lastName=Json2, age=0), User(firstName=Tom3, lastName=Json3, age=0), User(firstName=Tom4, lastName=Json4, age=0)]
    2021-03-12 15:16:18.215 30145-30269/com.example.firstapp I/System.out: 第三次取值 = [User(firstName=Tom, lastName=Json, age=0), User(firstName=Tom1, lastName=Json1, age=0), User(firstName=Tom2, lastName=Json2, age=0), User(firstName=Tom3, lastName=Json3, age=0), User(firstName=Tom4, lastName=sunjian, age=0)]
    

數(shù)據(jù)庫升級

在開發(fā)過程中難免會碰到數(shù)據(jù)庫表結(jié)構(gòu)的改變,碰到這種情況,我們就需要對數(shù)據(jù)庫表的結(jié)構(gòu)進行升級,sqlite支持的數(shù)據(jù)庫表結(jié)構(gòu)的操作說明:

SQLite supports a limited subset of ALTER TABLE. The ALTER TABLE command in SQLite allows the user to rename a table or to add a new column to an existing table. It is not possible to rename a column, remove a column, or add or remove constraints from a table.

大致意思就是說,支持字段的添加和修改,不支持刪除。所以針對數(shù)據(jù)庫結(jié)構(gòu)的升級就只是正對字段的添加和修改

  • 數(shù)據(jù)庫表字段添加

我們正對之前Entity類進行操作,新增加一個e_mail字段

@Entity()
data class User(
    @ColumnInfo(name = "first_name")
    val firstName: String,
    @ColumnInfo(name = "last_name")
    var lastName: String,
    @ColumnInfo(name = "age")
    var age: Int = 0



) {
    @PrimaryKey(autoGenerate = true)
    var uid: Int = 0

    @ColumnInfo
    var email: String = ""http://新增的數(shù)據(jù)庫字段
}
  • 升級數(shù)據(jù)庫的版本
@Database(entities = [User::class],version = 2)//版本號有原來的 1 -> 2
abstract class AppDatabase : RoomDatabase() {

    abstract fun getUserDao(): UserDao

}
  • 數(shù)據(jù)庫對象中添加遷移配置
 val db = Room.databaseBuilder(
        App.context,
        AppDatabase::class.java,
        "snukaisens"
    ).addMigrations(Migration_1_2())
        .build()


//此處需要重點說明一下,添加字段之后,需要設置 NOT NULL屬性,而且需要給默認值,要不然數(shù)據(jù)庫遷移過程中就會提示,創(chuàng)建的數(shù)據(jù)庫信息,和預期的不一致,從而導致閃退
 class Migration_1_2 : Migration(1,2){
        override fun migrate(database: SupportSQLiteDatabase) {
           database.execSQL("alter table User add column email TEXT NOT NULL DEFAULT ''")
        }
    }

數(shù)據(jù)庫 User表中添加的email字段就成功了,接下來說一下,修改字段,還是以上面的帶嗎為例,添加字段之后,發(fā)現(xiàn) email 這個字段好像不是很對,要修改成 e_mail,需要修改的內(nèi)容如下:


//1.第一處需要修改的地方  修改字段
@Entity()
data class User(
    @ColumnInfo(name = "first_name")
    val firstName: String,
    @ColumnInfo(name = "last_name")
    var lastName: String,
    @ColumnInfo(name = "age")
    var age: Int = 0



) {
    @PrimaryKey(autoGenerate = true)
    var uid: Int = 0

    @ColumnInfo
    var e_mail: String = ""http://字段重新修改
}

//2.修改版本號,記住這個版本
@Database(entities = [User::class],version = 2)
abstract class AppDatabase : RoomDatabase() {
    abstract fun getUserDao(): UserDao
}
//3.添加遷移配置
object DBHelper {
    val db = Room.databaseBuilder(
        App.context,
        AppDatabase::class.java,
        "snukaisens"
    ).addMigrations(Migration_1_2(),Migration_2_3())//注意此處需要傳的參數(shù)是可變參數(shù),直接添加就行了,不需要把之前的都刪了
        .build()

    class Migration_1_2 : Migration(1,2){
        override fun migrate(database: SupportSQLiteDatabase) {
           database.execSQL("alter table User add column email TEXT NOT NULL DEFAULT ''")
        }

    }
    //修改字段 Migration 這個類構(gòu)造中傳的字段就是 數(shù)據(jù)庫的升級前和升級后的版本號,一定要和 2 中的數(shù)字對應
    class Migration_2_3 : Migration(2,3){
        override fun migrate(database: SupportSQLiteDatabase) {
            database.execSQL("alter table User rename column email to e_mail")
        }

    }
}

到此,數(shù)據(jù)庫的修改就完成了。

數(shù)據(jù)庫加密

在實際開發(fā)過程過程中,設計到安全問題,手機root過之后,用戶可以隨意拿到數(shù)據(jù)庫文件,進行查看。針對一些敏感的數(shù)據(jù)庫數(shù)據(jù),需要對其進行加密。本文采用

庫進行操作,操作很簡單。

//引入依賴
implementation "net.zetetic:android-database-sqlcipher:4.4.2"
...

//添加配置
 private val factory = SupportFactory("xxxxxx".toByteArray())//此處xxxxxx 用戶根據(jù)自己的情況自己配置
    val db = Room.databaseBuilder(
        App.context,
        AppDatabase::class.java,
        "snukaisens"
    ).addMigrations(Migration_1_2(), Migration_2_3())
        .openHelperFactory(factory)//添加factory
        .build()

這樣就可以了,數(shù)據(jù)庫加密就完成了,是不是很簡單

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • AngularJS是什么?AngularJs(后面就簡稱ng了)是一個用于設計動態(tài)web應用的結(jié)構(gòu)框架。首先,它是...
    200813閱讀 1,804評論 0 3
  • 關于Mongodb的全面總結(jié) MongoDB的內(nèi)部構(gòu)造《MongoDB The Definitive Guide》...
    中v中閱讀 32,315評論 2 89
  • 概要 64學時 3.5學分 章節(jié)安排 電子商務網(wǎng)站概況 HTML5+CSS3 JavaScript Node 電子...
    阿啊阿吖丁閱讀 9,877評論 0 3
  • Room是一個對象關系映射(ORM)庫。Room抽象了SQLite的使用,可以在充分利用SQLite的同時訪...
    tuacy閱讀 54,480評論 16 109
  • ### 什么是Vue.js + Vue.js 是目前最火的一個前端框架,React是最流行的一個前端框架(Reac...
    JLong閱讀 1,373評論 0 0

友情鏈接更多精彩內(nèi)容