[英]How to pre-populate an existing DB with data?
我正在构建的应用程序目前有一个Workout database
。
Workout DB
有一个Workout table
和一个WoroutSets table
。
这两个表中的数据是通过用户输入插入(保存)的。
这就是存储一些数据的地方。
顺便说一句,我想将名为WorkoutList
pre-populated data
放入此Workout DB
中。
我为此查阅了文档。
我导出Workout.db
并在 DB Browser 中为 SQLite 预填充了数据。
我们将根据文档使用createFromAsset("Workout.db")
。 (还没试过)
但是,我担心的是现有应用程序的Work DB
与添加了WorkoutList table
的Workout DB之间是否存在冲突。
假设您想保留每个应用程序用户输入的workout s 和workoutsets ,那么您不希望使用createFromAsset
覆盖它们。
相反,我怀疑您想要做的是引入一个新的锻炼列表表,该表根据作为资产提供的数据库在锻炼列表中填充预定义/预先存在的行。 在这种情况下,您不想使用createFromAsset
方法(尽管您可能会从资产创建第二个数据库,将其附加到原始数据库,然后合并数据——这会比需要的更复杂)。
您还必须考虑如何处理新安装,在这种情况下将没有现有的用户输入workout和workoutsets ,在这种情况下您可以使用createFromAsset
方法。 但是,您不会想要任何其他用户的workout和workoutsets行。
基于这个假设,也许可以考虑这个演示,它引入了一个新表(锻炼列表),其数据是从资产中检索的,在其他表(锻炼和锻炼集)中维护原始用户数据,但是对于新安装的应用程序,从资产中创建数据库.
锻炼
@Entity
class Workout {
@PrimaryKey
Long workoutId=null;
String workoutName;
Workout(){};
@Ignore
Workout(String workoutName) {
this.workoutId=null;
this.workoutName=workoutName;
}
}
WorkoutSet (复数未使用但很容易更改)
@Entity
class WorkoutSet {
@PrimaryKey
Long workoutSetId=null;
String workoutSetName;
long workoutIdMap;
WorkoutSet(){}
@Ignore
WorkoutSet(String workoutSetName, long parentWorkout) {
this.workoutSetId=null;
this.workoutSetName = workoutSetName;
this.workoutIdMap = parentWorkout;
}
}
WorkkoutList (新表)
@Entity
class WorkoutList {
@PrimaryKey
Long workoutListId=null;
String workoutListName;
}
AllDAO (只是为了完整性)
@Dao
abstract class AllDAO {
@Insert(onConflict = OnConflictStrategy.IGNORE)
abstract long insert(Workout workout);
@Insert(onConflict = OnConflictStrategy.IGNORE)
abstract long insert(WorkoutSet workoutSet);
@Query("SELECT count(*) FROM workout")
abstract long getNumberOfWorkouts();
}
WorkoutDatabase @Database
注解为class
@Database(entities = {Workout.class,WorkoutSet.class, WorkoutList.class /*<<<<<<<<<< ADDED for V2 */}, exportSchema = false, version = MainActivity.DATABASE_VERSION)
abstract class WorkoutDatabase extends RoomDatabase {
abstract AllDAO getAllDAO();
private static Context passed_context;
private static volatile WorkoutDatabase INSTANCE;
static WorkoutDatabase getInstance(Context context) {
passed_context = context;
if (INSTANCE==null && MainActivity.DATABASE_VERSION == 1) {
INSTANCE = Room.databaseBuilder(context,WorkoutDatabase.class,MainActivity.DATABASE_NAME)
.allowMainThreadQueries()
.build();
}
if (INSTANCE ==null && MainActivity.DATABASE_VERSION > 1) {
INSTANCE = Room.databaseBuilder(context,WorkoutDatabase.class,MainActivity.DATABASE_NAME)
.allowMainThreadQueries()
.createFromAsset(MainActivity.DATABASE_ASSET_NAME) /* so new App installs use asset */
.addMigrations(MIGRATION_1_TO_2) /* to handle migration */
.build();
}
return INSTANCE;
}
static Migration MIGRATION_1_TO_2 = new Migration(1,2) {
@SuppressLint("Range")
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
/* Create the new table */
database.execSQL("CREATE TABLE IF NOT EXISTS `WorkoutList` (`workoutListId` INTEGER, `workoutListName` TEXT, PRIMARY KEY(`workoutListId`))");
/* Cater for copying the data from the asset */
String tempDBName = "temp_" + MainActivity.DATABASE_NAME; /* name of the temporary/working database NOT an SQLITE TEMP database */
String newTableName = "workoutlist"; /* The table name */
String qualifiedNewTableName = tempDBName + "." + newTableName; /* The fully qualified new table name for the attached temp/wrk db */
String tempDBPath = passed_context.getDatabasePath(MainActivity.DATABASE_NAME).getParent() + File.separator + tempDBName; /* path to temp/wrk db */
try {
/* Copy the asset to a second DB */
InputStream asset = passed_context.getAssets().open(MainActivity.DATABASE_ASSET_NAME); /* open the asset */
File tempDB_File = new File(tempDBPath); /* File for temp/wrk database */
OutputStream tempdb = new FileOutputStream(tempDB_File); /* now an output stream ready for the copy */
int bufferLength = 1024 * 8; /* length of buffer set to 8k */
byte[] buffer = new byte[bufferLength]; /* the buffer for the copy */
/* copy the temp/wrk database from the asset to it's location */
while(asset.read(buffer) > 0) {
tempdb.write(buffer);
}
/* clean up after copy */
tempdb.flush();
tempdb.close();
asset.close();
/*Use the temporary/working database to populate the actual database */
/* Issues with WAL file change because migration is called within a transaction as per */
/* java.lang.IllegalStateException: Write Ahead Logging (WAL) mode cannot be enabled or disabled while there are transactions in progress. .... */
/* SO COMMENTED OUT */
//database.execSQL("ATTACH DATABASE '" + tempDBPath + "' AS " + tempDBName);
//database.execSQL("INSERT INTO " + newTableName + " SELECT * FROM " + qualifiedNewTableName);
//database.execSQL("DETACH " + tempDBName);
/* Alternative to ATTACH */
SQLiteDatabase assetdb = SQLiteDatabase.openDatabase(tempDB_File.getPath(),null,SQLiteDatabase.OPEN_READONLY);
Cursor csr = assetdb.query(newTableName,null,null,null,null,null,null);
ContentValues cv = new ContentValues();
while (csr.moveToNext()) {
cv.clear();
for (String s: csr.getColumnNames()) {
cv.put(s,csr.getString(csr.getColumnIndex(s)));
}
database.insert(newTableName,SQLiteDatabase.CONFLICT_IGNORE,cv);
}
assetdb.close();
tempDB_File.delete(); /* delete the temporary/working copy of the asset */
} catch (Exception e) {
/* handle issues here e.g. no asset, unable to read/write an so on */
e.printStackTrace();
}
}
};
}
createFromAssets
从资产中复制数据库。MainActivity对数据库执行某些操作以确保打开/访问数据库的活动代码
public class MainActivity extends AppCompatActivity {
public static final String DATABASE_NAME = "workout.db";
public static final int DATABASE_VERSION = 2;
public static final String DATABASE_ASSET_NAME = "testit.db"/*DATABASE_NAME*/ /* could be different */;
WorkoutDatabase wdb;
AllDAO dao;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
wdb = WorkoutDatabase.getInstance(this);
dao = wdb.getAllDAO();
if (dao.getNumberOfWorkouts() < 1) addInitialData();
//wdb.close(); // FORCE close so WAL is full checkpointed (i.e. no -wal or -shm)
}
private void addInitialData() {
String prefix = String.valueOf(System.currentTimeMillis()); // to differentiate devices
dao.insert(new Workout(prefix + "-W001"));
dao.insert(new Workout(prefix + "-W002"));
dao.insert(new WorkoutSet(prefix + "-WS001",1));
dao.insert(new WorkoutSet(prefix + "-WS002",2));
}
}
testit.db SQLite 数据库文件经过修改以引入新的workoutList表。 这首先从版本 1 的 App 运行中复制,然后添加新表(根据从 WorkoutDataabase_Impl class 的createAllTables方法复制的SQL在生成的 java(也就是根据 Room 的期望的确切表))。
-WRONG
已添加到数据的末尾(有助于证明某些观点)新表
Navicat 是 SQLite 使用的工具而不是 DB Browser。
Run 1以版本 1 运行以填充数据库
@Database(entities = {Workout.class,WorkoutSet.class/*, WorkoutList.class*/ /*<<<<<<<<<< ADDED for V2 */}, exportSchema = false, version = MainActivity.DATABASE_VERSION)
应用检查显示:-
和
Run 2版本 2 和引入新的workoutlist表
应用检查显示:-
在 VERSION 2运行 3 次新安装(应用程序卸载后)
结论
因此,新安装的应用程序通过createFromAsset
正确地复制了资产数据库,而如果迁移,则仅从资产复制锻炼列表表中的新数据。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.