繁体   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