簡體   English   中英

更新應用程序時如何將新數據添加到android房間數據庫?

[英]How to add new data to android room database when updating app?

我正在使用 Room 數據庫制作一個 android 應用程序。

我的計划是在設備上安裝時用一些初始數據預填充數據庫,

用戶可以編輯它並在每個表上插入新行。

用戶的新行 ID 將從例如 10000 開始,

(我的問題的重點),后來我想在最多 9999 的行中添加更多數據

當用戶更新應用程序時,我可以這樣做嗎? 或者還有其他方法嗎?

也許我應該嘗試將 csv 文件導入房間數據庫

謝謝!!

我從應用程序資產中預填充的代碼

Room.databaseBuilder(application, AppDatabase::class.java, DB_NAME)
                 .createFromAsset("database/appdatabase.db")
                 .build()

為了讓用戶開始,如果你有@PrimaryKey(autogenerate = true)那么在准備原始預填充數據時,你可以輕松設置下一個要使用的用戶 ID。

例如,如果實體是:-

@Entity
data class User(
    @PrimaryKey(autoGenerate = true)
    val userId: Long=0,
    val userName: String,
)

即 userid 和 userName 是列,第一次運行時,您希望第一個應用程序提供的用戶 ID 為 10000,然后您可以在 SQLite 工具中使用(作為示例)以下內容:-

CREATE TABLE IF NOT EXISTS `User` (`userId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `userName` TEXT);
INSERT INTO User (userName) VALUES('Fred'),('Mary'),('Sarah'); /* Add Users as required */
INSERT INTO User VALUES(10000 -1,'user to be dropped'); /* SETS the next userid value to be 10000 */
DELETE FROM user WHERE userid >= 10000 - 1; /* remove the row added */
  1. 根據Entity創建表(SQL是從生成的java@AppDatabase_Impl拷貝過來的)
  2. 加載一些用戶
  3. 添加一個 userId 為 9999 (10000 - 1) 的用戶,這會導致 SQLite 在用戶表的 SQLite 系統表 sqlite_sequnce 中記錄 9999。
  4. 刪除為設置序列號而添加的用戶。

以下,如果在上述之后使用,則演示了執行上述操作的結果:-

/* JUST TO DEMONSTRATE WHAT THE ABOVE DOES */
/* SHOULD NOT BE RUN as the first App user is added */
SELECT * FROM sqlite_sequence;
INSERT INTO user (username) VALUES('TEST USER FOR DEMO DO NOT ADD ME WHEN PREPARING DATA');
SELECT * FROM user;

第一個查詢:-

在此處輸入圖片說明

  • 即 SQLite 已將值9999存儲在名為user的表的 sqlite_sequence 表中

第二個查詢顯示添加第一個用戶時會發生什么:-

在此處輸入圖片說明

回顧一下,運行 1-4 會准備預填充的數據庫,以便第一個添加的 App 用戶的用戶 ID 為 10000。

添加新數據

您確實必須決定如何添加新數據。 你想要一個csv嗎? 您想提供更新的 AppDatabase 嗎? 使用所有數據還是僅使用新數據? 您是否需要保留任何現有的用戶/應用程序輸入數據? 新安裝怎么樣? 具體細節很可能很重要。

這是您如何管理此問題的示例。 這使用更新的預填充數據並假設要保留應用程序用戶輸入的現有數據。

一個重要的值是提供的用戶 ID 和通過正在使用的應用程序輸入的用戶 ID 之間的 10000 分界。 因此,已使用的用戶實體是:-

@Entity
data class User(
    @PrimaryKey(autoGenerate = true)
    val userId: Long=0,
    val userName: String,
) {
    companion object {
        const val USER_DEMARCATION = 10000;
    }
}

有些 Dao 可能有用,有些則在UserDao類中使用:-

@Dao
abstract class UserDao {
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    abstract fun insert(user: User): Long
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    abstract fun insert(users: List<User>): LongArray
    @Query("SELECT * FROM user")
    abstract fun getAllUsers(): List<User>
    @Query("SELECT * FROM user WHERE userid < ${User.USER_DEMARCATION}")
    abstract fun getOnlySuppliedUsers(): List<User>
    @Query("SELECT * FROM user WHERE userid >= ${User.USER_DEMARCATION}")
    abstract fun getOnlyUserInputUsers(): List<User>
    @Query("SELECT count(*) > 0 AS count FROM user WHERE userid >= ${User.USER_DEMARCATION}")
    abstract fun isAnyInputUsers(): Long
    @Query("SELECT max(userid) + 1 FROM user WHERE userId < ${User.USER_DEMARCATION}")
    abstract fun getNextSuppliedUserid(): Long
}

@Database 類AppDatabase :-

@Database(entities = [User::class],version = AppDatabase.DATABASE_VERSION, exportSchema = false)
abstract class AppDatabase: RoomDatabase() {
    abstract fun getUserDao(): UserDao

    companion object {
        const val DATABASE_NAME = "appdatabase.db"
        const val DATABASE_VERSION: Int = 2 /*<<<<<<<<<<*/

        private  var instance: AppDatabase? = null
        private var contextPassed: Context? = null
        fun getInstance(context: Context): AppDatabase {
            contextPassed = context
            if (instance == null) {
                instance = Room.databaseBuilder(
                    context,
                    AppDatabase::class.java,
                    DATABASE_NAME
                )
                    .allowMainThreadQueries()
                    .addMigrations(migration1_2)
                    .createFromAsset(DATABASE_NAME)
                    .build()
            }
            return instance as AppDatabase
        }
        val migration1_2 = object: Migration(1,2) {
            val assetFileName = "appdatabase.db" /* NOTE appdatabase.db not used to cater for testing */
            val tempDBName = "temp_" + assetFileName
            val bufferSize = 1024 * 4
            @SuppressLint("Range")
            override fun migrate(database: SupportSQLiteDatabase) {
                val asset = contextPassed?.assets?.open(assetFileName) /* Get the asset as an InputStream */
                val tempDBPath = contextPassed?.getDatabasePath(tempDBName) /* Deduce the file name to copy the database to */
                val os = tempDBPath?.outputStream() /* and get an OutputStream for the new version database */

                /* Copy the asset to the respective file (OutputStream) */
                val buffer = ByteArray(bufferSize)
                while (asset!!.read(buffer,0,bufferSize) > 0) {
                    os!!.write(buffer)
                }
                /* Flush and close the newly created database file */
                os!!.flush()
                os.close()
                /* Close the asset inputStream */
                asset.close()
                /* Open the new database */
                val version2db = SQLiteDatabase.openDatabase(tempDBPath.path,null,SQLiteDatabase.OPEN_READONLY)
                /* Grab all of the supplied rows */
                val v2csr = version2db.rawQuery("SELECT * FROM user WHERE userId < ${User.USER_DEMARCATION}",null)
                /* Insert into the actual database ignoring duplicates (by userId) */
                while (v2csr.moveToNext()) {
                    database.execSQL("INSERT OR IGNORE INTO user VALUES(${v2csr.getLong(v2csr.getColumnIndex("userId"))},'${v2csr.getString(v2csr.getColumnIndex("userName"))}')",)
                }
                /* close cursor and the newly created database */
                v2csr.close()
                version2db.close()
                tempDBPath.delete() /* Delete the temporary database file */
            }
        }
    }
  • 為了方便和簡潔,在主線程上進行了測試,因此.allowMainThreadQueries

  • 可以看出,使用了從 1 到 2 的遷移:-

  • 采用資產 appdatabase.db 2nd 版本(另外 3 個“提供的”用戶已添加“使用:-

     CREATE TABLE IF NOT EXISTS `User` (`userId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `userName` TEXT NOT NULL); INSERT INTO User (userName) VALUES('Fred'),('Mary'),('Sarah'); /* Add Users as required */ INSERT INTO User (userName) VALUES('Tom'),('Elaine'),('Jane'); /*+++++ Version 2 users +++++*/ INSERT INTO User VALUES(10000 -1,'user to be dropped'); /* SETS the next userid value to be 10000 */ DELETE FROM user WHERE userid >= 10000 - 1; /* remove the row added */```

因此,首先資產 appdatabase.db 包含原始數據(3 個提供的用戶)並且序列號設置為 9999。

如果應用程序具有數據庫版本 1,則復制此預先填充的數據庫。

應用程序的用戶可以添加自己的用戶名,用戶名將被分配 10000、10001 ...

當下一個版本發布時,資產 appdatabase 會相應地更改,保持 9999 序列號忽略任何 App 輸入用戶 ID(它們是未知的),並且數據庫版本從 1 更改為 2。

更新 App 時會調用 migration1_2。 如果新用戶安裝了該應用程序,則 Room 的 createFromAsset 會立即從資產創建數據庫。

當用戶更新應用程序時,我可以這樣做嗎? 或者還有其他方法嗎?

如上所述,可以在更新應用程序和增加數據庫版本時完成。 可以通過其他方式完成,但檢測更改的數據可能會變得復雜。

也許我應該嘗試將 csv 文件導入房間數據庫?

CSV 沒有處理新安裝和固有版本檢查的優勢。

我可以在不更改數據庫架構的情況下使用遷移嗎?

是的,如上所示。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM