最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Android數(shù)據(jù)庫Room的實際使用過程總結(jié)

 更新時間:2025年01月07日 10:38:45   作者:記憶里模糊的小時候  
這篇文章主要給大家介紹了關(guān)于Android數(shù)據(jù)庫Room的實際使用過程,詳細介紹了如何創(chuàng)建實體類、數(shù)據(jù)訪問對象(DAO)和數(shù)據(jù)庫抽象類,需要的朋友可以參考下

前言

最近我負責(zé)開發(fā)一個基于Android系統(tǒng)的平板應(yīng)用程序,在項目中涉及到數(shù)據(jù)庫操作的部分,我們最終決定采用Room數(shù)據(jù)庫框架來實現(xiàn)。在實際使用過程中,我遇到了一些挑戰(zhàn)和問題,現(xiàn)在我想將這些經(jīng)驗記錄下來,以便未來參考和改進。

一、Room的基本使用

1.項目配置

在開發(fā)這個Android項目時,我決定將數(shù)據(jù)庫操作代碼獨立成一個模塊,這樣做有助于保持代碼的整潔和模塊化。在這個模塊中,我選擇了Kotlin作為編程語言,并使用了Kotlin 1.5.21版本。為了支持Kotlin開發(fā)和編譯,我需要在項目中包含兩個插件:kotlin-android 和 kotlin-kapt。這兩個插件分別負責(zé)Kotlin代碼的Android特定功能支持和注解處理,確保代碼能夠正確編譯和運行。

plugins {
    id 'com.android.library'
    id 'kotlin-android'
    id 'kotlin-kapt'
}

采用了Room框架,具體版本為2.3.0。由于Room框架在不同版本之間可能存在API差異,因此在這里特別指出我所使用的版本,以便于在遇到問題時能夠準(zhǔn)確地查找和解決問題,同時也使用到了協(xié)程,所有依賴如下:

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    // Room數(shù)據(jù)庫版本
    def room_version = "2.3.0"
    implementation "androidx.room:room-runtime:$room_version"
    // Kapt
    kapt "androidx.room:room-compiler:$room_version"
    // room-ktx
    implementation "androidx.room:room-ktx:$room_version"
    // 協(xié)程
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2"
}

2.創(chuàng)建實體類(Entity)

在Room數(shù)據(jù)庫框架中,實體類是用來映射數(shù)據(jù)庫表的。每個實體類代表一個數(shù)據(jù)庫表,而實體類的屬性則對應(yīng)表中的列。以下是一個使用Kotlin語言編寫的Airport實體類的示例代碼,其中id字段被標(biāo)記為主鍵:

import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "airports") // 指定表名
data class Airport(
    @PrimaryKey(autoGenerate = true) val id: Int, // 主鍵,自動生成
    val name: String, // 機場名稱
    val city: String, // 所在城市
    val country: String // 所在國家
)

3.創(chuàng)建數(shù)據(jù)訪問對象(DAO - Data Access Object)

DAO 用于定義訪問數(shù)據(jù)庫的方法,比如插入、查詢、更新、刪除等操作。以下是針對 Airport實體類創(chuàng)建的 AirportDao示例:

import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update
import androidx.room.Delete

@Dao
interface AirportDao {
    // 插入單個Airport對象
    @Insert
    suspend fun insert(airport: Airport)

    // 插入多個Airport對象
    @Insert
    suspend fun insertAll(vararg airports: Airport)

    // 根據(jù)ID查詢Airport對象
    @Query("SELECT * FROM airports WHERE id = :id")
    suspend fun getAirportById(id: Int): Airport?

    // 更新Airport對象
    @Update
    suspend fun update(airport: Airport)

    // 刪除單個Airport對象
    @Delete
    suspend fun delete(airport: Airport)

    // 刪除所有Airport對象
    @Query("DELETE FROM airports")
    suspend fun deleteAll()
}

在這個AirportDao接口中:

  • @Dao 注解標(biāo)記這個接口為一個DAO接口。
  • @Insert注解用于定義插入操作的方法。insert()方法可以插入單個Airport對象,而insertAll()方法可以插入多個Airport對象。
  • @Query 注解用于定義自定義SQL查詢的方法。getAirportById()方法通過ID查詢單個Airport對象。
  • @Update 注解用于定義更新操作的方法。update()方法更新一個Airport對象。
  • @Delete注解用于定義刪除操作的方法。delete()方法可以刪除單個Airport對象,而deleteAll()方法可以刪除所有Airport對象。
  • suspend關(guān)鍵字用于標(biāo)記一個函數(shù)為掛起函數(shù)(suspend function),這是Kotlin協(xié)程(coroutines)的一個重要特性。掛起函數(shù)可以暫停和恢復(fù)其執(zhí)行,而不會阻塞線程

這些方法定義了與Airport實體類對應(yīng)的數(shù)據(jù)庫表進行交互的基本操作。Room框架會在編譯時自動實現(xiàn)這些接口方法,開發(fā)者無需手動編寫實現(xiàn)代碼。

4. 創(chuàng)建數(shù)據(jù)庫抽象類(Database)

在Room數(shù)據(jù)庫框架中,你需要創(chuàng)建一個繼承自RoomDatabase的抽象類,這個類將作為數(shù)據(jù)庫的訪問入口,并定義與實體類和DAO的關(guān)聯(lián)。以下是一個示例代碼,展示了如何創(chuàng)建這樣的數(shù)據(jù)庫類,并與Airport實體類和AirportDao接口關(guān)聯(lián):

import androidx.room.Database
import androidx.room.RoomDatabase
import androidx.room.TypeConverters

// 定義數(shù)據(jù)庫的版本
@Database(entities = [Airport::class], version = 1, exportSchema = false)
@TypeConverters(YourTypeConverters::class) // 如果有自定義類型轉(zhuǎn)換器,在這里指定
abstract class AppDatabase : RoomDatabase() {

    // 提供獲取DAO實例的方法
    abstract fun airportDao(): AirportDao

    // Companion object to create an instance of AppDatabase
    companion object {
        // Singleton instance of the database
        @Volatile
        private var instance: AppDatabase? = null

        // Method to get the database instance
        fun getInstance(context: Context): AppDatabase {
            return instance ?: synchronized(this) {
                instance ?: buildDatabase(context).also { inst ->
                    instance = inst
                }
            }
        }

        // Method to build the database
        private fun buildDatabase(context: Context): AppDatabase {
            return Room.databaseBuilder(
                context.applicationContext,
                AppDatabase::class.java,
                "app_database"
            ).build()
        }
    }
}

在這個AppDatabase類中:

  • @Database注解定義了數(shù)據(jù)庫包含的實體類(entities)和數(shù)據(jù)庫版本(version)。exportSchema屬性用于控制是否導(dǎo)出數(shù)據(jù)庫的schema文件,通常在開發(fā)階段設(shè)置為true,而在生產(chǎn)環(huán)境中設(shè)置為false。
  • @TypeConverters注解用于指定一個或多個類,這些類包含自定義的類型轉(zhuǎn)換器,如果需要將非標(biāo)準(zhǔn)類型(如Date或URL)存儲在數(shù)據(jù)庫中,這些轉(zhuǎn)換器是必需的。
  • abstract fun airportDao(): AirportDao提供了一個抽象方法,用于獲取AirportDao的實例,這樣我們就可以在數(shù)據(jù)庫類中執(zhí)行對Airport表的操作。
  • companion
    object提供了一個單例模式的實現(xiàn),用于獲取AppDatabase的實例。getInstance()方法確保整個應(yīng)用中只有一個數(shù)據(jù)庫實例被創(chuàng)建。buildDatabase()方法用于實際構(gòu)建和配置數(shù)據(jù)庫。

**請注意!**你需要將YourTypeConverters::class替換為實際包含自定義類型轉(zhuǎn)換器的類的名稱,如果你沒有自定義類型轉(zhuǎn)換器,可以省略@TypeConverters注解。此外,context參數(shù)需要從你的應(yīng)用上下文傳遞給getInstance()方法,以確保數(shù)據(jù)庫正確地與應(yīng)用的生命周期關(guān)聯(lián)。

5. 使用數(shù)據(jù)庫

在Android的Activity中使用數(shù)據(jù)庫進行操作時,可以在協(xié)程中執(zhí)行這些操作,以避免阻塞主線程。以下是在Activity中使用協(xié)程與Room數(shù)據(jù)庫進行交互的簡單示例代碼片段:

import android.os.Bundle
import android.util.Log
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModelProvider
import kotlinx.coroutines.*

class AirportActivity : AppCompatActivity() {

    private val viewModel by viewModels<AirportViewModel>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_airport)

        // 啟動協(xié)程來插入數(shù)據(jù)
        lifecycleScope.launch {
            viewModel.insertAirport(Airport(0, "Moonshot International", "Shanghai", "China"))
        }

        // 啟動協(xié)程來查詢數(shù)據(jù)
        lifecycleScope.launch {
            val airport = viewModel.getAirportById(1) // 假設(shè)ID為1
            airport.observe(this@AirportActivity, { airport ->
                Log.d("AirportActivity", "Airport Name: ${airport?.name}")
            })
        }
    }
}

在這個Activity中:

  • lifecycleScope是一個與Activity生命周期綁定的協(xié)程作用域,它確保協(xié)程在Activity銷毀時取消。
  • launch是一個協(xié)程構(gòu)建器,用于啟動一個新的協(xié)程。
  • insertAirport方法在協(xié)程中被調(diào)用,用于插入新的機場信息。
    getAirportById方法返回一個LiveData<Airport?>對象,它在協(xié)程中被觀察,以便在機場信息變化時更新UI。

請注意,AirportViewModel需要正確實現(xiàn),并且包含insertAirport和getAirportById方法。這些方法應(yīng)該在ViewModel中使用viewModelScope而不是lifecycleScope,因為viewModelScope是與ViewModel的生命周期綁定的,而不是Activity。

以下是AirportViewModel的示例實現(xiàn):

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.*

class AirportViewModel : ViewModel() {

    private val database = AppDatabase.getInstance(applicationContext) // 假設(shè)這是全局可訪問的context
    private val airportDao = database.airportDao()

    fun insertAirport(airport: Airport) {
        viewModelScope.launch {
            airportDao.insert(airport)
        }
    }

    fun getAirportById(id: Int): LiveData<Airport?> {
        return liveData(viewModelScope.coroutineContext + Dispatchers.IO) {
            emit(airportDao.getAirportById(id))
        }
    }
}

至此Room簡單的使用已經(jīng)說完了,這些步驟構(gòu)成了Room數(shù)據(jù)庫在Android應(yīng)用中的簡單使用流程。Room提供了一個抽象層,幫助開發(fā)者以更聲明式和類型安全的方式進行數(shù)據(jù)庫操作,同時利用協(xié)程簡化了異步編程。

二、Room使用過程遇到的問題

1.聲明表中字段可以為null

import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "airports") // 指定表名
data class Airport(
    @PrimaryKey(autoGenerate = true) val id: Int, // 主鍵,自動生成
    val name: String, // 機場名稱
    val city: String, // 所在城市
    val country: String // 所在國家
)

如果在使用Room數(shù)據(jù)庫時,需要在實體類中允許某些字段存儲空值,可以直接將這些字段聲明為可空類型。這樣,即使在插入數(shù)據(jù)時這些字段的值為空,數(shù)據(jù)庫操作也能正常進行。具體來說,只需在實體類中將相應(yīng)的變量聲明為String?、Int?等可空類型,Room就會允許這些字段在數(shù)據(jù)庫中存儲空值,代碼如下:

import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "airports") // 指定表名
class Airport {
    @PrimaryKey(autoGenerate = true)
    var id: Int = 0 // 主鍵,自動生成
    var name: String? = null // 機場名稱
    var city: String? = null// 所在城市
    var country: String? = null // 所在國家
}

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

當(dāng)你在Room數(shù)據(jù)庫的實體類中添加了一個新的字段后,如果在運行應(yīng)用時遇到了崩潰,并且出現(xiàn)了異常信息,這通常是因為Room數(shù)據(jù)庫的遷移問題。Room需要知道如何處理數(shù)據(jù)庫結(jié)構(gòu)的變化,比如添加、刪除或修改字段。如果沒有正確處理這些變化,Room在嘗試訪問數(shù)據(jù)庫時就會拋出異常,異常信息如下:

Room cannot verify the data integrity, Looks like vou’ve changed schema but forgot to update the version number, You can simply . fix this by increasing the version number.

遇到數(shù)據(jù)庫結(jié)構(gòu)變更時,通常有兩種處理方法:

第一種卸載并重新安裝應(yīng)用:這是一種簡單直接的方法,通過卸載應(yīng)用再重新安裝,應(yīng)用將創(chuàng)建全新的數(shù)據(jù)庫,從而自動包含所有新的表結(jié)構(gòu)和字段變更。另一種方法是進行數(shù)據(jù)庫升級,下面是數(shù)據(jù)庫升級的步驟:

  • 更新實體類:在實體類中添加新的字段。例如,如果你想為Airport實體類添加一個新字段,你可以直接聲明這個字段為可空類型,如val newField: String? = null。
  • 增加數(shù)據(jù)庫版本號:在@Database注解中增加版本號。例如,如果你的數(shù)據(jù)庫當(dāng)前版本是1,那么在添加新字段后,將版本號增加到2:@Database(entities = [Airport::class], version = 2) 。
  • 創(chuàng)建Migration遷移類:在RoomDatabase中定義Migration對象,指定如何從舊版本遷移到新版本。例如,為Airport實體類添加新字段的遷移可以這樣定義:
val MIGRATION_1_2: Migration = object : Migration(1, 2) {
   override fun migrate(database: SupportSQLiteDatabase) {
        // 執(zhí)行SQL
       database.execSQL("ALTER TABLE airports ADD COLUMN newField TEXT")
   }
}
  • 將Migration添加到數(shù)據(jù)庫構(gòu)建中:在構(gòu)建數(shù)據(jù)庫時,通過addMigrations方法添加Migration對象。例如
val database = Room.databaseBuilder(
    context.applicationContext,
    AppDatabase::class.java, 
    "app_database"
).addMigrations(MIGRATION_1_2).build()

這樣,當(dāng)應(yīng)用啟動時,Room會自動執(zhí)行Migration中定義的遷移操作。
通過這些步驟,你可以平滑地將Room數(shù)據(jù)庫升級到新版本,同時添加新的字段。如果用戶之前安裝的數(shù)據(jù)庫版本較低,Room會按照定義的Migration順序依次執(zhí)行,直到達到最新的數(shù)據(jù)庫版本。

3.如何關(guān)聯(lián)外鍵ForeignKey

發(fā)現(xiàn)有些人不知道什么是外鍵:這里簡單說明一下:

外鍵的主要作用如下:

  • 建立關(guān)聯(lián)關(guān)系:用于在不同表之間建立聯(lián)系,清晰體現(xiàn)實體之間的對應(yīng)關(guān)系,如機場與跑道的所屬關(guān)系,方便進行多表聯(lián)合查詢等操作。

  • 維護數(shù)據(jù)完整性:防止在插入或更新數(shù)據(jù)時出現(xiàn)無效數(shù)據(jù),確保子表中的外鍵值在父表的主鍵值中存在或為 NULL,保證數(shù)據(jù)的準(zhǔn)確性和一致性。

  • 實現(xiàn)級聯(lián)操作:定義級聯(lián)規(guī)則,當(dāng)父表中的記錄發(fā)生刪除或更新時,子表中對應(yīng)的記錄可按規(guī)則自動進行相應(yīng)操作,確保數(shù)據(jù)在不同表之間的協(xié)調(diào)一致。

  • 約束數(shù)據(jù)變化:通過參照完整性約束,控制數(shù)據(jù)在表之間的更新和刪除傳播方式,保證數(shù)據(jù)的修改和更新符合特定的業(yè)務(wù)邏輯和要求。

在 Room 中聲明外鍵可以通過在實體類中使用@ForeignKey注解來實現(xiàn)。以下是一個示例,展示了如何在機場表和機場跑道表之間聲明外鍵關(guān)聯(lián):

  • 定義機場表實體類
import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "airport")
data class Airport(
    @PrimaryKey(autoGenerate = true)
    var airportId: Int = 0,
    var airportName: String = ""
)
  • 定義機場跑道表實體類(Runway)并聲明外鍵
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.PrimaryKey

@Entity(
    tableName = "runway",
    foreignKeys = [ForeignKey(
        entity = Airport::class,
        parentColumns = ["airportId"],
        childColumns = ["airportIdFK"],
        onDelete = ForeignKey.CASCADE
    )]
)
data class Runway(
    @PrimaryKey(autoGenerate = true)
    var runwayId: Int = 0,
    var airportIdFK: Int = 0,
    var runwayName: String = ""
)

在上述 Runway 實體類中,使用 @ForeignKey 注解來聲明外鍵關(guān)系,各參數(shù)含義和 Java 版本中的一致:

  • entity:指定關(guān)聯(lián)的實體類型,這里關(guān)聯(lián)的是 Airport 類。
  • parentColumns:表示在關(guān)聯(lián)的父實體(即 Airport)中對應(yīng)的主鍵列名,此處為 airportId。
  • childColumns:代表在當(dāng)前實體(Runway)中作為外鍵的列名,也就是 airportIdFK。
  • onDelete:定義了當(dāng)父表(Airport 表)中對應(yīng)的主鍵記錄被刪除時的行為,這里設(shè)置為 CASCADE,意味著級聯(lián)刪除,比如刪除某個機場記錄時,與之關(guān)聯(lián)的跑道記錄也會自動刪除。除了 CASCADE 之外,還有以下幾種常見的類型及其含義:

-NO_ACTION含義:當(dāng)父表中的記錄被刪除或更新時,子表中的外鍵列不做任何操作,這可能會導(dǎo)致子表中的外鍵引用無效的父鍵值,從而產(chǎn)生孤立的數(shù)據(jù),破壞數(shù)據(jù)的完整性。
示例:在 文章表 和 評論表 的關(guān)聯(lián)中,如果使用 NO_ACTION,當(dāng)一篇文章被刪除時,評論表中對應(yīng)的文章外鍵值不會改變,仍然保留原來的文章 ID,即使該文章已經(jīng)不存在了,這就導(dǎo)致了評論表中的這些評論與實際不存在的文章產(chǎn)生了孤立的關(guān)聯(lián)。
SET_NULL含義:當(dāng)父表中的記錄被刪除或更新時,子表中對應(yīng)的外鍵列的值將被設(shè)置為 NULL。
示例:假設(shè)有 用戶表 和 訂單表,訂單表 中的 用戶ID 是外鍵關(guān)聯(lián)到 用戶表 的主鍵。當(dāng)一個用戶被刪除時,該用戶的所有訂單記錄中的 用戶ID 字段將被設(shè)置為 NULL,表示這些訂單與任何用戶都不再關(guān)聯(lián),但訂單記錄本身仍然保留在 訂單表 中。
SET_DEFAULT含義:當(dāng)父表中的記錄被刪除或更新時,子表中對應(yīng)的外鍵列的值將被設(shè)置為其默認值。
示例:若 訂單表 中的 用戶ID 外鍵字段有一個默認值為 0,當(dāng)關(guān)聯(lián)的用戶被刪除時,該用戶的所有訂單記錄中的 用戶ID 將被設(shè)置為 0,以此來表示一種特殊的狀態(tài)或無關(guān)聯(lián)的情況。
RESTRICT含義:當(dāng)父表中的記錄被刪除或更新時,如果子表中存在對應(yīng)的關(guān)聯(lián)記錄,則拒絕父表的刪除或更新操作,從而防止出現(xiàn)孤立的子記錄,確保數(shù)據(jù)的一致性和完整性。
示例:在 部門表 和 員工表 的關(guān)系中,員工表 通過外鍵關(guān)聯(lián)到 部門表 的主鍵。如果試圖刪除一個部門,而該部門下還有員工,那么由于 RESTRICT 約束,數(shù)據(jù)庫將不允許執(zhí)行這個刪除操作,避免出現(xiàn)員工所屬部門不存在的不合理情況。

4.使用事務(wù)@Transaction

在 Room 中,事務(wù)是一種重要的機制,用于確保多個數(shù)據(jù)庫操作的原子性,即要么所有操作都成功執(zhí)行,數(shù)據(jù)庫狀態(tài)被完整更新;要么所有操作都失敗回滾,數(shù)據(jù)庫保持初始狀態(tài),從而有效地維護數(shù)據(jù)的一致性。以下是關(guān)于 Room 中事務(wù)的詳細介紹:

事務(wù)的必要性

  • 在實際的數(shù)據(jù)庫操作中,常常會有多個相關(guān)的操作需要作為一個整體來執(zhí)行。例如,在一個銀行轉(zhuǎn)賬系統(tǒng)中,從一個賬戶扣除一定金額并在另一個賬戶增加相應(yīng)金額,這兩個操作必須同時成功或同時失敗,否則就會導(dǎo)致數(shù)據(jù)不一致,如賬戶余額出現(xiàn)錯誤等問題。事務(wù)機制正是為了滿足這種需求而設(shè)計的,它能夠保證在復(fù)雜的操作場景下數(shù)據(jù)的準(zhǔn)確性和完整性。

使用方法

以下是一個使用事務(wù)進行多表查詢的例子,還以Airport和Runway這兩個實體類為例,它們之間存在關(guān)聯(lián)關(guān)系。

@Dao
interface AirportRunwayDao {
    @Query("SELECT * FROM airports WHERE id = :airportId")
    fun getAirport(airportId: Int): Airport?

    @Query("SELECT * FROM runways WHERE airportId = :airportId")
    fun getRunways(airportId: Int): List<Runway>

    // 事務(wù)性查詢操作
    @Transaction
    fun getAirportWithRunways(airportId: Int): Pair<Airport?, List<Runway>> {
        // 這里的代碼將在一個事務(wù)中執(zhí)行
        val airport = getAirport(airportId)
        val runways = getRunways(airportId)
        return Pair(airport, runways)
    }
}

5.數(shù)據(jù)庫文件的位置

在Room數(shù)據(jù)庫中,創(chuàng)建AppDatabase對象時,可以指定數(shù)據(jù)庫文件的名稱,這個名稱也是數(shù)據(jù)庫文件的名字。默認情況下,Room數(shù)據(jù)庫文件存儲在應(yīng)用的內(nèi)部存儲目錄下的特定子目錄中。如果需要更改數(shù)據(jù)庫文件的存儲位置,可以通過指定具體的文件路徑來實現(xiàn)。這樣,數(shù)據(jù)庫文件就會被創(chuàng)建在指定的路徑下,而不是默認的內(nèi)部存儲位置。代碼如下:

private fun buildDatabase(context: Context): AppDatabase {
            val dbPath = "${context.getExternalFilesDir(null)?.absolutePath}/database/test.db"
            return Room.databaseBuilder(
                context.applicationContext,
                AppDatabase::class.java,
                dbPath 
            ).build()
        }

這樣數(shù)據(jù)庫文件存在的位置,就會放到指定目錄下。

6.打開已存在的數(shù)據(jù)庫

在大多數(shù)應(yīng)用場景中,Room數(shù)據(jù)庫的標(biāo)準(zhǔn)使用方法已經(jīng)足夠。但在本次項目中,我們需要軟件具備打開本地已有數(shù)據(jù)庫文件或?qū)胪獠繑?shù)據(jù)庫文件的功能。操作步驟與常規(guī)配置相似:

  • 創(chuàng)建實體類(Entity):定義一個實體類來映射數(shù)據(jù)庫中的表結(jié)構(gòu)。
  • 創(chuàng)建數(shù)據(jù)訪問對象(DAO):定義一個接口,用于執(zhí)行數(shù)據(jù)庫的增刪改查等操作。

與常規(guī)使用的主要區(qū)別在于,需要將待打開的數(shù)據(jù)庫文件放置在指定目錄下。在初始化Room數(shù)據(jù)庫時,指定數(shù)據(jù)庫文件的路徑:

  • 如果指定路徑下已存在數(shù)據(jù)庫文件,Room將直接使用該文件。
  • 如果指定路徑下沒有數(shù)據(jù)庫文件,Room將創(chuàng)建一個新的數(shù)據(jù)庫文件。
    這樣,我們就能夠?qū)崿F(xiàn)對本地或外部數(shù)據(jù)庫文件的訪問和管理。

打開外部數(shù)據(jù)時遇到的問題

當(dāng)遇到

IllegalStateException: Pre-packaged database has an invalid schema: airport
Expected…
…表結(jié)構(gòu)信息
Found:
…表結(jié)構(gòu)信息

當(dāng)遇到類似 “IllagelStateException: Pre-packaged database has an invalid schena: Excepted… Found:” 這樣的報錯時,其背后的原因通常是預(yù)打包數(shù)據(jù)庫(也就是你準(zhǔn)備打開的外部數(shù)據(jù)文件對應(yīng)的數(shù)據(jù)庫)的架構(gòu)與 Room 所期望的架構(gòu)出現(xiàn)了不匹配的情況。
那這里所說的數(shù)據(jù)庫架構(gòu),涵蓋了表結(jié)構(gòu)、列定義以及約束等多個方面的內(nèi)容。常見的導(dǎo)致架構(gòu)不匹配的因素有以下幾種:

  • 一是字段可空聲明不一致。比如在 Room中通過實體類定義某個字段是非空的,但在預(yù)打包數(shù)據(jù)庫里對應(yīng)的該字段卻允許為空,或者反之,這種差異就會造成架構(gòu)不一致。
  • 二是數(shù)據(jù)類型不一致??赡茉趯嶓w類中定義某個字段為 Integer 類型,然而預(yù)打包數(shù)據(jù)庫里對應(yīng)列的數(shù)據(jù)類型卻是TEXT,不同的數(shù)據(jù)類型設(shè)置會讓 Room 在驗證數(shù)據(jù)庫架構(gòu)時判定為不匹配。
  • 三是外鍵約束不一致。例如在 Room 的實體類中定義了兩張表之間通過外鍵建立了特定的關(guān)聯(lián)關(guān)系,并且設(shè)置了相應(yīng)的外鍵約束規(guī)則,像刪除操作時的級聯(lián)方式等,但在預(yù)打包數(shù)據(jù)庫里對應(yīng)的表之間的外鍵約束情況與之不同,這同樣會引發(fā)架構(gòu)方面的問題。

當(dāng)出現(xiàn)這類報錯后,我們需要仔細對比異常日志里呈現(xiàn)的兩個表結(jié)構(gòu),查找究竟是哪個地方出現(xiàn)了不一致的情況。一旦發(fā)現(xiàn)了問題所在,接下來就要采取相應(yīng)的解決措施。要么對 Room 中的 Entity 實體類進行修改,使其表結(jié)構(gòu)、字段定義以及約束等各方面與預(yù)打包數(shù)據(jù)庫的實際架構(gòu)相符;要么對預(yù)打包的數(shù)據(jù)庫文件本身進行調(diào)整,從而讓二者的結(jié)構(gòu)能夠達成一致。只有在確保這兩個結(jié)構(gòu)完全一致的前提下,才能夠成功連接數(shù)據(jù)庫,避免出現(xiàn)上述的報錯情況。

總結(jié)

到此這篇關(guān)于Android數(shù)據(jù)庫Room的實際使用的文章就介紹到這了,更多相關(guān)Android數(shù)據(jù)庫Room使用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • RecyclerView實現(xiàn)流式標(biāo)簽單選多選功能

    RecyclerView實現(xiàn)流式標(biāo)簽單選多選功能

    RecyclerView是Android一個更強大的控件,其不僅可以實現(xiàn)和ListView同樣的效果,還有優(yōu)化了ListView中的各種不足。這篇文章主要介紹了RecyclerView實現(xiàn)的流式標(biāo)簽單選多選功能,需要的朋友可以參考下
    2019-11-11
  • Android中可以作為Log開關(guān)的一些操作及安全性詳解

    Android中可以作為Log開關(guān)的一些操作及安全性詳解

    Android的調(diào)試好伙伴Log在調(diào)試時非常有用,基本可以看Log而無需單點調(diào)試,尤其對實時大數(shù)據(jù)量的設(shè)備調(diào)試尤其有效,下面這篇文章就來給大家詳細介紹關(guān)于Android中可以作為Log開關(guān)的一些操作及安全性的相關(guān)資料,需要的朋友可以參考下。
    2017-12-12
  • Android自定義控件之小說書架實現(xiàn)示例詳解

    Android自定義控件之小說書架實現(xiàn)示例詳解

    這篇文章主要為大家介紹了Android自定義控件之小說書架示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪方法
    2023-04-04
  • Android中獲取電池電量實例代碼

    Android中獲取電池電量實例代碼

    顯示當(dāng)前電池電量在特殊的需求下還是蠻有用的,本文寫了一個可以實現(xiàn)簡單的功能,感興趣的朋友可以參考下哈
    2013-06-06
  • Android中實現(xiàn)根據(jù)資源名獲取資源ID

    Android中實現(xiàn)根據(jù)資源名獲取資源ID

    這篇文章主要介紹了Android中實現(xiàn)根據(jù)資源名獲取資源ID,本文講解了使用文件名獲取資源ID的方法,需要的朋友可以參考下
    2015-01-01
  • 詳細講解AsyncTask使用說明(值得收藏)

    詳細講解AsyncTask使用說明(值得收藏)

    AsyncTask就相當(dāng)于Android給我們提供了一個多線程編程的一個框架,其介于Thread和Handler之間,我們?nèi)绻x一個AsyncTask,就需要定義一個類來繼承AsyncTask這個抽象類,并實現(xiàn)其唯一的一doInBackgroud 抽象方法,這篇文章主要介紹了AsyncTask詳解,需要的朋友可以參考下
    2024-01-01
  • Android ViewPager 的使用總結(jié)

    Android ViewPager 的使用總結(jié)

    這篇文章主要介紹了Android ViewPager 的相關(guān)資料,并附實例代碼,,需要的朋友可以參考下
    2016-09-09
  • android底層去掉虛擬按鍵的實例講解

    android底層去掉虛擬按鍵的實例講解

    今天小編就為大家分享一篇android底層去掉虛擬按鍵的實例講解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-07-07
  • Android組件之BroadcastReceiver廣播接收者

    Android組件之BroadcastReceiver廣播接收者

    這篇文章主要為大家介紹了Android組件之BroadcastReceiver廣播接收者實現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-04-04
  • Android自定義可編輯、刪除的側(cè)滑LisitView

    Android自定義可編輯、刪除的側(cè)滑LisitView

    這篇文章主要為大家詳細介紹了Android自定義可編輯、刪除的側(cè)滑LisitView,感興趣的小伙伴們可以參考一下
    2016-07-07

最新評論

安阳市| 拉孜县| 慈利县| 罗城| 永济市| 临泽县| 新竹县| 乌恰县| 榕江县| 永兴县| 曲周县| 绵竹市| 东光县| 道孚县| 榆树市| 信阳市| 长治市| 应城市| 玉门市| 连江县| 龙胜| 涡阳县| 巴彦淖尔市| 合江县| 怀安县| 尤溪县| 泸定县| 大理市| 九龙城区| 福泉市| 托克逊县| 五河县| 东方市| 沽源县| 柳林县| 富裕县| 云梦县| 遂平县| 峨眉山市| 罗甸县| 灌阳县|