[英]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.